美文网首页
nest.js 集成数据迁移方案 sequelize、umzug

nest.js 集成数据迁移方案 sequelize、umzug

作者: 吴占超 | 来源:发表于2023-01-31 14:12 被阅读0次

    ORM采用sequelize
    迁移采用umzug
    数据库基于mysql
    不说废话,上干货。

    # 安装依赖
    $ yarn add @nestjs/sequelize sequelize sequelize-typescript mysql2
    $ yarn add umzug -D
    $ yarn add @types/sequelize -D
    $ yarn add cross-env -D
    # 创建迁移文件夹
    $ mkdir database
    

    数据库迁移配置

    • database/config.json
    {
      "local": {
        "port": 3306,
        "host": "127.0.0.1",
        "database": "xxxxx",
        "username": "root",
        "password": "xxxx",
        "dialect": "mysql",
        "define": {
          "charset": "utf8"
        },
        "logging": false
      },
      "development": {
        ...
      },
      "stage": {
        ...
      },
      "production": {
        ...
      },
      "test": {
        ...
      }
    }
    
    

    执行文件

    使用ts-node执行ts脚本

    • database/migrator.js
    require('ts-node/register');
    
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    require('./umzug').migrator.runAsCLI();
    
    • database/migrator-seed.js
    require('ts-node/register');
    
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    require('./umzug-seed').migrator.runAsCLI();
    
    • database/umzug.ts
    import { Umzug, SequelizeStorage } from 'umzug';
    import { Sequelize } from 'sequelize';
    // config 加载
    import config from './config.json';
    import { get, snakeCase, replace } from 'lodash';
    import path from 'path';
    import fs from 'fs';
    
    const rtlEnv = process.env.NODE_ENV;
    const sequelizeConfig: any = get(config, rtlEnv || 'development');
    console.log(`[umzug]:db:${sequelizeConfig.database}`);
    const sequelize = new Sequelize(sequelizeConfig);
    
    /**
     * 默认模版替换
     * @param filepath 路径
     * @returns
     */
    const findTemplate = (filepath: string) => {
      const temp = fs
        .readFileSync(path.join('database/template/table.ts'))
        .toString();
      const names = filepath.split('.');
      const name = snakeCase(names[names.length - 2]);
      return replace(temp, /\[tableName\]/g, name);
    };
    
    export const migrator = new Umzug({
      migrations: {
        glob: ['migrations/*.ts', { cwd: __dirname }],
      },
      create: {
        template: (filepath) => [[filepath, findTemplate(filepath)]],
        folder: path.join('database/migrations/'),
      },
      context: sequelize,
      storage: new SequelizeStorage({
        sequelize,
      }),
      logger: console,
    });
    
    export type Migration = typeof migrator._types.migration;
    
    
    • database/umzug-seed.ts
    import { Sequelize } from 'sequelize';
    import { Umzug, SequelizeStorage } from 'umzug';
    // config 加载
    import config from './config.json';
    import { get, snakeCase, replace } from 'lodash';
    import path from 'path';
    import fs from 'fs';
    
    const rtlEnv = process.env.NODE_ENV;
    const sequelizeConfig: any = get(config, rtlEnv || 'development');
    console.log(`[umzug]:db:${sequelizeConfig.database}`);
    const sequelize = new Sequelize(sequelizeConfig);
    
    /**
     * 默认模版替换
     * @param filepath 路径
     * @returns
     */
    const findTemplate = (filepath: string) => {
      const temp = fs
        .readFileSync(path.join('database/template/seed.ts'))
        .toString();
      const names = filepath.split('.');
      const name = snakeCase(names[names.length - 2]);
      return replace(temp, /\[tableName\]/g, name);
    };
    
    export const migrator = new Umzug({
      migrations: {
        glob: ['seeds/*.ts', { cwd: __dirname }],
      },
      create: {
        template: (filepath) => [[filepath, findTemplate(filepath)]],
        folder: path.join('database/seeds/'),
      },
      context: sequelize,
      storage: new SequelizeStorage({
        sequelize,
        modelName: 'SequelizeData',
      }),
      logger: console,
    });
    
    export type Migration = typeof migrator._types.migration;
    
    
    • database/utils/default-column.ts
      默认列 本地开发主外键设计
    import Sequelize from 'sequelize';
    
    const { DATE, STRING, INTEGER } = Sequelize;
    
    export default {
      id: { type: STRING(50), primaryKey: true },
      // Creating two objects with the same value will throw an error. The unique property can be either a
      // boolean, or a string. If you provide the same string for multiple columns, they will form a
      created_at: {
        type: DATE,
        defaultValue: Sequelize.fn('now'),
        comment: '创建时间',
      },
      created_id: {
        type: STRING(50),
        defaultValue: '',
        comment: '创建人id',
      },
      updated_at: {
        type: DATE,
        defaultValue: Sequelize.fn('now'),
        comment: '修改时间',
      },
      updated_id: {
        type: STRING(50),
        comment: '修改人id',
      },
      deleted_at: { type: DATE, comment: '删除时间' },
      deleted_id: {
        type: STRING(50),
        comment: '删除人id',
      },
      business_code: {
        type: STRING(500),
        comment: '业务编码权限用',
      },
      remark: {
        type: STRING(500),
        comment: '备注',
      },
      version: {
        type: INTEGER,
        comment: 'BaseTable.version',
      },
      enable_flag: {
        type: INTEGER,
        comment: '状态 1启用 0停用默认1',
        defaultValue: 1,
      },
    };
    
    export const references = (tableName: string, keyName = 'id') => {
      if (process.env.NODE_ENV === 'production') {
        return undefined;
      }
      return {
        model: {
          tableName,
        },
        keyName,
      };
    };
    
    

    自定义模版

    • database/template/table.ts
    import { MigrationFn } from 'umzug';
    import { Sequelize } from 'sequelize';
    // import { DataTypes } from 'sequelize';
    import defaultCloumns from '../utils/default-column';
    
    export const up: MigrationFn<Sequelize> = async ({ context: sequelize }) => {
      return await sequelize.getQueryInterface().createTable('[tableName]', {
        ...defaultCloumns,
      });
    };
    
    export const down: MigrationFn<Sequelize> = async ({ context: sequelize }) => {
      return await sequelize.getQueryInterface().dropTable('[tableName]');
    };
    
    
    • database/template/seed.ts
    /* eslint-disable @typescript-eslint/no-unused-vars */
    import { MigrationFn } from 'umzug';
    import { Sequelize } from 'sequelize';
    // import { DataTypes } from 'sequelize';
    
    export const up: MigrationFn<Sequelize> = async ({ context: sequelize }) => {
      return await sequelize.getQueryInterface().bulkInsert('[tableName]', [{}]);
    };
    
    export const down: MigrationFn<Sequelize> = async ({ context: sequelize }) => {
      return await sequelize.getQueryInterface().bulkDelete('[tableName]', {
        where: {
          id: [],
        },
      });
    };
    
    

    命令

    umzug 结构迁移
    seed 数据迁移
    -h = 帮助
    -up = 升级
    -down = 降级
    -create = 创建

    {
      "script": {
        "umzug:h": "cross-env NODE_ENV=local node database/migrator.js -h",
        "umzug:u": "cross-env NODE_ENV=local node database/migrator.js up",
        "umzug:d": "cross-env NODE_ENV=local node database/migrator.js down",
        "umzug:c": "cross-env NODE_ENV=local node database/migrator.js create",
        "seed:h": "cross-env NODE_ENV=local node database/migrator-seed.js -h",
        "seed:u": "cross-env NODE_ENV=local node database/migrator-seed.js up",
        "seed:d": "cross-env NODE_ENV=local node database/migrator-seed.js down",
        "seed:c": "cross-env NODE_ENV=local node database/migrator-seed.js create",
        "umzug-dev:u": "cross-env NODE_ENV=development node database/migrator.js up",
        "umzug-dev:d": "cross-env NODE_ENV=development node database/migrator.js down",
        "umzug-prod:u": "cross-env NODE_ENV=production node database/migrator.js up",
        "umzug-prod:d": "cross-env NODE_ENV=production node database/migrator.js down",
        "seed-prod:u": "cross-env NODE_ENV=production node database/migrator-seed.js up",
        "seed-prod:d": "cross-env NODE_ENV=production node database/migrator-seed.js down"
      }
    }
    

    创建命令

    $  yarn umzug:c --name user.ts
    

    error

    yarn run v1.22.19
    $ cross-env NODE_ENV=local node database/migrator.js create --name user.ts
    /Users/wuzhanchao/Documents/万达项目组/好房推荐官/source/wd-nest-manager/node_modules/ts-node/src/index.ts:859
        return new TSError(diagnosticText, diagnosticCodes, diagnostics);
               ^
    TSError: ⨯ Unable to compile TypeScript
    TSError: ⨯ Unable to compile TypeScript:
    database/umzug.ts:4:20 - error TS2732: Cannot find module './config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.
    
    4 import config from './config.json';
    

    tsconfig.json
    增加配置项

    {
      "compilerOptions": {
        "resolveJsonModule": true
        "esModuleInterop": true
        ......
    

    相关文章

      网友评论

          本文标题:nest.js 集成数据迁移方案 sequelize、umzug

          本文链接:https://www.haomeiwen.com/subject/sunvhdtx.html