TypeORM/nest - running migrations with `useFactory`

Viewed 1132

I have the app in nestjs with the app.module.ts containing the following configuration:

import { ConfigModule, ConfigService } from '@nestjs/config';
import {
  TypeOrmModuleAsyncOptions,
  TypeOrmModuleOptions,
} from '@nestjs/typeorm';
import { DataSource } from 'typeorm';  

export const typeOrmAsyncConfig: TypeOrmModuleAsyncOptions = {
  imports: [ConfigModule],
  inject: [ConfigService],
  name: 'nameA',
  useFactory: (configService: ConfigService) => ({
    type: 'postgres',
    host: configService.get('DB_HOST'),
    port: parseInt(configService.get('DB_PORT') || '5432'),
    username: configService.get('DB_USER'),
    password: configService.get('DB_PASSWORD'),
    database: configService.get('DB_NAME'),
    entities: [__dirname + './../**/*.entity{.ts,.js}'],
    migrations: [__dirname + '/../database/migrations/*{.ts,.js}'],
    synchronize: false,
    cli: {
      entitiesDir: __dirname + './../**/*.entity{.ts,.js}',
    },
    ssl: configService.get('DB_SSL') === 'true',
  }),
};

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRootAsync(typeOrmAsyncConfig),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

I have typeorm installed globally and have migration files generated in the specified paths above, but, I get a dataSource not found error:

$> typeorm migration:run

output:

Runs all pending migrations.

Options:
  -h, --help         Show help                                         [boolean]
  -d, --dataSource   Path to the file where your DataSource instance is defined.
                                                                      [required]
  -t, --transaction  Indicates if transaction should be used or not for
                     migration run. Enabled by default.     [default: "default"]
  -v, --version      Show version number                               [boolean]

Missing required argument: dataSource

What I don't know how to do is how do I export a datasource when I'm using the useFactory inside of the app.module.ts and solve this issue?

2 Answers

Since typeorm 0.3.x cli needs a datasource and ormconfig.json is no more supported. Also cli commands have changed too. You can define separate configurations for nestjs and typeorm-cli.

// src/config/database-config.ts
import 'dotenv/config';
import { DataSourceOptions } from 'typeorm';

const databaseConfig: DataSourceOptions = {
  name: 'default',
  type: 'better-sqlite3',
  database: process.env.DB_NAME,
  entities: [__dirname + '/../entities/*.entity.ts'],
  migrations: [__dirname + '/../migrations/*{.ts,.js}'],
  synchronize: false,
  logging: true,
  migrationsRun: true
};

export default databaseConfig;

then for typeorm cli

// src/config/data-source.ts
import { DataSource } from 'typeorm';
import databaseConfig from './database-config';

export const AppDataSource = new DataSource(databaseConfig);

and for nestjs app-module

// src/config/typeorm-config.ts
// like so: TypeOrmModule.forRootAsync(typeOrmAsyncConfig)

import { ConfigModule, ConfigService } from '@nestjs/config';
import {
  TypeOrmModuleAsyncOptions,
  TypeOrmModuleOptions,
} from '@nestjs/typeorm';
import databaseConfig from './database-config';

export const typeOrmConfig: TypeOrmModuleOptions = databaseConfig;

export const typeOrmAsyncConfig: TypeOrmModuleAsyncOptions = {
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: async (): Promise<TypeOrmModuleOptions> => {
    return databaseConfig;
  },
};

package.json scripts (change yarn run if needed)

{
"typeorm:cli": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli -d src/config/data-source.ts",
"migration:generate": "yarn run typeorm:cli migration:generate",
"migration:create": "yarn run typeorm:cli migration:create",
"migration:run": "yarn run typeorm:cli migration:run",
"migration:revert": "yarn run typeorm:cli migration:revert"
}

for example if you need to generate a new migration complete path needed in terminal

yarn run migration:generate src/migrations/newMigrationName

For migration up there seems to be a convenient way, but, since the answer is partial, I'm not marking it done:

{
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (configService: ConfigService) => ({
    type: 'postgres',
    host: configService.get('DB_HOST'),
    port: parseInt(configService.get('DB_PORT') || '4432'),
    username: configService.get('DB_USER'),
    password: configService.get('DB_PASSWORD'),
    database: configService.get('DB_NAME'),
    entities: [MyEntity],
    migrations: ['./**/*.migration.js'],
    synchronize: false,
    ssl: configService.get('DB_SSL') === 'true',
  }),
  dataSourceFactory: async (options: DataSourceOptions) => {
    const dataSource = await new DataSource(options).initialize();
    await dataSource.runMigrations();

    return dataSource;
  },
}  

If new migrations are added, they should be applied since the server is restarted.

Related