logologo
开始
手册
开发
插件
API
English
简体中文
开始
手册
开发
插件
API
English
简体中文
logologo

快速入门

插件开发概述
编写第一个插件
项目目录结构

服务端开发

概述
Plugin 插件
Collections 数据表
Database 数据库操作
DataSourceManager 数据源管理
ResourceManager 资源管理
ACL 权限控制
Middleware 中间件
Cache 缓存
Event 事件
Context 请求上下文
Migration 升级脚本
Logger日志
I18n 国际化
Command 命令行
CronJobManager 定时任务管理
Test 测试

客户端开发

概述
Plugin 插件
Context 上下文
Router 路由
ACL 权限控制
DataSourceManager 数据源管理
Resource 资源
Request 请求
Styles & Themes 样式与主题
Logger 日志
I18n 国际化
Test 测试

其他

插件升级指南
语言列表
依赖管理
构建
Previous PageContext 请求上下文
Next PageLogger日志

#Migration 升级脚本

在 NocoBase 插件的开发与更新过程中,插件的数据库结构或配置可能会发生不兼容的变化。为了保证升级的平滑执行,NocoBase 提供了 Migration 机制,通过编写 migration 文件来处理这些变更。本文将带你系统了解 Migration 的使用方法和开发流程。

#Migration 的概念

Migration 是插件升级时自动执行的脚本,用于解决以下问题:

  • 数据表结构调整(新增字段、修改字段类型等)
  • 数据迁移(如字段值的批量更新)
  • 插件配置或内部逻辑更新

Migration 的执行时机分为三类:

类型触发时机执行场景
beforeLoad所有插件配置加载前
afterSync数据表配置与数据库同步之后(表结构已变更)
afterLoad所有插件配置加载后

#创建 Migration 文件

Migration 文件应放在插件目录下的 src/server/migrations/*.ts 中。NocoBase 提供 create-migration 命令快速生成 migration 文件。

yarn nocobase create-migration [options] <name>

可选参数

参数说明
--pkg <pkg>指定插件包名
--on [on]指定执行时机,可选 beforeLoad、afterSync、afterLoad

示例

$ yarn nocobase create-migration update-ui --pkg=@nocobase/plugin-client

生成的 migration 文件路径如下:

/nocobase/packages/plugins/@nocobase/plugin-client/src/server/migrations/20240107173313-update-ui.ts

文件初始内容:

import { Migration } from '@nocobase/server';

export default class extends Migration {
  on = 'afterLoad'; // 'beforeLoad' | 'afterSync' | 'afterLoad'
  appVersion = '<0.19.0-alpha.3';

  async up() {
    // 在这里编写升级逻辑
  }
}

⚠️ appVersion 用于标识升级所针对的版本,小于指定版本的环境会执行该 migration。

#编写 Migration

在 Migration 文件中,你可以通过 this 访问以下常用属性和 API,方便操作数据库、插件及应用实例:

常用属性

  • this.app
    当前 NocoBase 应用实例。可用于访问全局服务、插件或配置。

    const config = this.app.config.get('database');
  • this.db
    数据库服务实例,提供对模型(Tables)操作的接口。

    const users = await this.db.getRepository('users').findAll();
  • this.plugin
    当前插件实例,可用于访问插件的自定义方法。

    const settings = this.plugin.customMethod();
  • this.sequelize
    Sequelize 实例,可直接执行原生 SQL 或事务操作。

    await this.sequelize.transaction(async (transaction) => {
      await this.sequelize.query('UPDATE users SET active = 1', { transaction });
    });
  • this.queryInterface
    Sequelize 的 QueryInterface,常用于修改表结构,例如新增字段、删除表等。

    await this.queryInterface.addColumn('users', 'age', {
      type: this.sequelize.Sequelize.INTEGER,
      allowNull: true,
    });

编写 Migration 示例

import { Migration } from '@nocobase/server';

export default class extends Migration {
  on = 'afterSync';
  appVersion = '<0.19.0-alpha.3';

  async up() {
    // 使用 queryInterface 添加字段
    await this.queryInterface.addColumn('users', 'nickname', {
      type: this.sequelize.Sequelize.STRING,
      allowNull: true,
    });

    // 使用 db 访问数据模型
    const users = await this.db.getRepository('users').findAll();
    for (const user of users) {
      user.nickname = user.username;
      await user.save();
    }

    // 执行 plugin 的自定义方法
    await this.plugin.customMethod();
  }
}

除了上面列出的常用属性,Migration 还提供丰富的 API,详细文档请参考 Migration API。

#触发 Migration

Migration 的执行由 nocobase upgrade 命令触发:

$ yarn nocobase upgrade

升级时,系统会根据 Migration 的类型和 appVersion 判断执行顺序。

#测试 Migration

在插件开发中,建议使用 Mock Server 测试 migration 是否正确执行,避免破坏真实数据。

import { createMockServer, MockServer } from '@nocobase/test';

describe('Migration Test', () => {
  let app: MockServer;

  beforeEach(async () => {
    app = await createMockServer({
      plugins: ['my-plugin'], // 插件名称
      version: '0.18.0-alpha.5', // 升级前版本
    });
  });

  afterEach(async () => {
    await app.destroy();
  });

  test('run upgrade migration', async () => {
    await app.runCommand('upgrade');
    // 编写验证逻辑,例如检查字段是否存在、数据是否迁移成功
  });
});

Tip: 使用 Mock Server 可以快速模拟升级场景,并对 Migration 执行顺序和数据变更进行验证。

#开发实践建议

  1. 拆分 Migration
    每次升级尽量生成一个 migration 文件,保持原子性,便于排查问题。
  2. 指定执行时机
    根据操作对象选择 beforeLoad、afterSync 或 afterLoad,避免依赖未加载的模块。
  3. 注意版本控制
    使用 appVersion 明确 migration 适用的版本,防止重复执行。
  4. 测试覆盖
    在 Mock Server 上验证 migration 后,再在真实环境中执行升级。