Nest: Can't init TypeOrmModule using factory and forRootAsync

Created on 25 Sep 2018  路  14Comments  路  Source: nestjs/nest

I'm submitting a...


[ ] Regression 
[x ] Bug report
[ ] Feature request
[x ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Documented forRootAsync way doesn't work with postgres settings.
Either I do something wrong or types are wrong.

screen shot 2018-09-25 at 12 13 01 pm

Argument of type '{ imports: (typeof ConfigModule)[]; inject: (typeof ConfigService)[]; useFactory: (configService: ConfigService) => Promise<{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }>; }' is not assignable to parameter of type 'TypeOrmModuleAsyncOptions'.
  Types of property 'useFactory' are incompatible.
    Type '(configService: ConfigService) => Promise<{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }>' is not assignable to type '(...args: any[]) => ({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<MysqlConnectionOptions>) | ({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<...>) | ... 7 more ... | Promise<...>'.
      Type 'Promise<{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }>' is not assignable to type '({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<MysqlConnectionOptions>) | ({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<...>) | ... 7 more ... | Promise<...>'.
        Type 'Promise<{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }>' is not assignable to type 'Promise<TypeOrmModuleOptions>'.
          Type '{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }' is not assignable to type 'TypeOrmModuleOptions'.
            Type '{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }' is not assignable to type '{ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<MongoConnectionOptions>'.
              Type '{ type: string; host: string; port: string; username: string; password: string; database: string; entities: (typeof User)[]; }' is not assignable to type 'Partial<MongoConnectionOptions>'.
                Types of property 'type' are incompatible.
                  Type 'string' is not assignable to type '"mongodb"'.

Expected behavior


Should be able to use TypeOrmModule.forRootAsync with useFactory and postgres connection settings

Minimal reproduction of the problem with instructions

import { Module, Global } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';
import { UserService } from './user/user.service';
import { ConfigModule } from '../config/config.module';
import { ConfigService } from '../config/config.service';
import { User } from './user/user.entity';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'postgres',
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
        username: configService.get('DB_USER'),
        password: configService.get('DB_PASSWORD'),
        database: configService.get('DB_NAME'),
        entities: [User],
      }),
    }),
    UserModule,
  ],
  providers: [UserService],
})
export class DatabaseModule {}

What is the motivation / use case for changing the behavior?

Environment


Nest version: 5.3.7


For Tooling issues:
- Node version: 10  
- Platform:  Mac 

Others:

Most helpful comment

Got here while I was looking for a solution to a similar problem, I had to cast the whole object literal to TypeOrmModuleOptions, otherwise it didn't work for me

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          type: 'postgres',
          host: config.postgresql.host,
          port: config.postgresql.port,
          username: config.postgresql.user,
          password: config.postgresql.pass,
          database: config.postgresql.db,
          entities: [__dirname + '/**/*.entity{.ts,.js}'],
          synchronize: config.env === 'dev',
        } as TypeOrmModuleOptions;
      },
    }),
  ],
})
export class AppModule {
}

All 14 comments

This issue comes from the typeorm library. Basically, you have to explicitly cast your type to union:

type: 'postgres' as 'postgres'

Are you saying:

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'postgres' as 'postgres', // <-- here?
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
        username: configService.get('DB_USER'),
        password: configService.get('DB_PASSWORD'),
        database: configService.get('DB_NAME'),
        entities: [User],
      }),
    }),
    UserModule,
  ],
  providers: [UserService],
})
export class DatabaseModule {}

wow, this is actually a life-saver..
However, how can i get the type (e.g., postgres) from a config file? Because i get an error if i try to do something like this:

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: configService.get('DB_DRIVER') as configService.get('DB_DRIVER')
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
        username: configService.get('DB_USER'),
        password: configService.get('DB_PASSWORD'),
        database: configService.get('DB_NAME'),
        entities: [User],
      }),
    }),
    UserModule,
  ],
  providers: [UserService],
})
export class DatabaseModule {}

I get a tremendous error when I run a build.... but the app still runs.

$ rimraf dist && tsc
src/database/database.module.ts:14:7 - error TS2322: Type '(configService: ConfigService) => Promise<{ type: "postgres"; host: string | number; port: string | number; username: string | number; password: string | number; database: string | number; schema: string | number; entities: (typeof Preference)[]; synchronize: string | number; }>' is not assignable to type '(...args: any[]) => ({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<MysqlConnectionOptions>) | ({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<...>) | ... 9 more ... | Promise<...>'.
  Type 'Promise<{ type: "postgres"; host: string | number; port: string | number; username: string | number; password: string | number; database: string | number; schema: string | number; entities: (typeof Preference)[]; synchronize: string | number; }>' is not assignable to type '({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<MysqlConnectionOptions>) | ({ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<...>) | ... 9 more ... | Promise<...>'.
    Type 'Promise<{ type: "postgres"; host: string | number; port: string | number; username: string | number; password: string | number; database: string | number; schema: string | number; entities: (typeof Preference)[]; synchronize: string | number; }>' is not assignable to type 'Promise<TypeOrmModuleOptions>'.
      Type '{ type: "postgres"; host: string | number; port: string | number; username: string | number; password: string | number; database: string | number; schema: string | number; entities: (typeof Preference)[]; synchronize: string | number; }' is not assignable to type 'TypeOrmModuleOptions'.
        Type '{ type: "postgres"; host: string | number; port: string | number; username: string | number; password: string | number; database: string | number; schema: string | number; entities: (typeof Preference)[]; synchronize: string | number; }' is not assignable to type '{ retryAttempts?: number; retryDelay?: number; keepConnectionAlive?: boolean; } & Partial<PostgresConnectionOptions>'.
          Type '{ type: "postgres"; host: string | number; port: string | number; username: string | number; password: string | number; database: string | number; schema: string | number; entities: (typeof Preference)[]; synchronize: string | number; }' is not assignable to type 'Partial<PostgresConnectionOptions>'.
            Types of property 'schema' are incompatible.
              Type 'string | number' is not assignable to type 'string'.
                Type 'number' is not assignable to type 'string'.

14       useFactory: async (configService: ConfigService) => ({
         ~~~~~~~~~~

  node_modules/@nestjs/typeorm/dist/interfaces/typeorm-options.interface.d.ts:16:5
    16     useFactory?: (...args: any[]) => Promise<TypeOrmModuleOptions> | TypeOrmModuleOptions;
           ~~~~~~~~~~
    The expected type comes from property 'useFactory' which is declared here on type 'TypeOrmModuleAsyncOptions'

error Command failed with exit code 2.

Got here while I was looking for a solution to a similar problem, I had to cast the whole object literal to TypeOrmModuleOptions, otherwise it didn't work for me

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          type: 'postgres',
          host: config.postgresql.host,
          port: config.postgresql.port,
          username: config.postgresql.user,
          password: config.postgresql.pass,
          database: config.postgresql.db,
          entities: [__dirname + '/**/*.entity{.ts,.js}'],
          synchronize: config.env === 'dev',
        } as TypeOrmModuleOptions;
      },
    }),
  ],
})
export class AppModule {
}

hi all, i had the same issue, and luca-nardelli solution worked for me also.
i was wondering if anyone has some insight into how this was the solution, what the underlying problem was?

Hi all, the problem is resolved when i casted to Number on 'port' propertie
port: Number(configService.get('DB_PORT')),

useFactory: async (configService: ConfigService) => {
        return ({
          type: 'postgres' as 'postgres',
          host: configService.get('DB_HOST'),
          port: Number(configService.get('DB_PORT')),
          username: configService.get('DB_USERNAME'),
          password: configService.get('DB_PASSWORD'),
          database: configService.get('DB_DATABASE'),
          entities: [__dirname + '/**/*.entity{.ts,.js}'],
          synchronize: false,
        });
        // as PostgresConnectionOptions;
      },
      inject: [ConfigService],
    })

Description

export declare type TypeOrmModuleOptions = {
    retryAttempts?: number;
    retryDelay?: number;
    keepConnectionAlive?: boolean;
} & Partial<ConnectionOptions>;

ConnectionOptions is how

export declare type ConnectionOptions = MysqlConnectionOptions | PostgresConnectionOptions | CockroachConnectionOptions | SqliteConnectionOptions | SqlServerConnectionOptions | OracleConnectionOptions | CordovaConnectionOptions | NativescriptConnectionOptions | ReactNativeConnectionOptions | SqljsConnectionOptions | MongoConnectionOptions | ExpoConnectionOptions;
PostgresConnectionCredentialsOptions is

export interface PostgresConnectionOptions extends BaseConnectionOptions, PostgresConnectionCredentialsOptions {
    /**
     * Database type.
     */
    readonly type: "postgres";
    /**
     * Schema name.
     */
    readonly schema?: string;
    /**
     * Replication setup.
     */
    readonly replication?: {
        /**
         * Master server used by orm to perform writes.
         */
        readonly master: PostgresConnectionCredentialsOptions;
        /**
         * List of read-from severs (slaves).
         */
        readonly slaves: PostgresConnectionCredentialsOptions[];
    };
    /**
     * The Postgres extension to use to generate UUID columns. Defaults to uuid-ossp.
     * If pgcrypto is selected, TypeORM will use the gen_random_uuid() function from this extension.
     * If uuid-ossp is selected, TypeORM will use the uuid_generate_v4() function from this extension.
     */
    readonly uuidExtension?: "pgcrypto" | "uuid-ossp";
}

And PostgresConnectionCredentialsOptions interface have the properitie

readonly port?: number;

therefore the documentation has a bug

@kodenix thanks for solution

Hey @kodenix ,
maybe you can submit a PR to this repo her ( https://github.com/nestjs/docs.nestjs.com ) in order to fix this documentation issue?! That would be awesome!

After doing luca-nardelli's solution, I now get:

Nest can't resolve dependencies of the TypeOrmModuleOptions (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context.

Any ideas?

@hbendix , try @kodenix 's solution, that's a really good solution and I am able to manage migrations using cli.

@hbendix be sure to implement the ConfigService that is imported in the code snipped above

@webcat12345, @johannesschobel thanks for your replies, I tried @kodenix solution and it worked a treat. Cheers!

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

menme95 picture menme95  路  3Comments

janckerchen picture janckerchen  路  3Comments

anyx picture anyx  路  3Comments

rlesniak picture rlesniak  路  3Comments

FranciZ picture FranciZ  路  3Comments