Question3r
Question3r

Reputation: 3832

NestJs - TypeORM configuration works but not with ConfigService

I would like to create a REST API with NestJs and TypeORM. In my app.module.ts I load the TypeORM module

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'postgres',
      database: 'api',
      entities: [`${__dirname}/**/*.entity.{ts,js}`],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

and it's working fine for now. I would like to load the configuration from an external .env file so from the docs

https://docs.nestjs.com/techniques/database#async-configuration

and from here

NestJS Using ConfigService with TypeOrmModule

I created a .env file in the root project directory with the following content

DATABASE_TYPE = postgres
DATABASE_HOST = localhost
DATABASE_PORT = 5432
DATABASE_USERNAME = postgres
DATABASE_PASSWORD = postgres
DATABASE_NAME = api
DATABASE_SYNCHRONIZE = true

Next I update my code to

@Module({
  imports: [
    ConfigModule.forRoot(),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        type: configService.get<any>('DATABASE_TYPE'),
        host: configService.get<string>('DATABASE_HOST'),
        port: configService.get<number>('DATABASE_PORT'),
        username: configService.get<string>('DATABASE_USERNAME'),
        password: configService.get<string>('DATABASE_PASSWORD'),
        database: configService.get<string>('DATABASE_NAME'),
        entities: [`${__dirname}/**/*.entity.{ts,js}`],
        synchronize: configService.get<boolean>('DATABASE_SYNCHRONIZE'),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Unfortunately I get this error on startup

[Nest] 28257   - 01/06/2020, 7:19:20 AM   [ExceptionHandler] Nest can't resolve dependencies of the TypeOrmModuleOptions (?). Please make sure that the argument ConfigService at index [0] is available in the TypeOrmCoreModule context.

Potential solutions:
- If ConfigService is a provider, is it part of the current TypeOrmCoreModule?
- If ConfigService is exported from a separate @Module, is that module imported within TypeOrmCoreModule?
  @Module({
    imports: [ /* the Module containing ConfigService */ ]
  })
 +1ms

When I log the configuration in my main.ts within the bootstrap function I get a correct configuration from the .env file.

How can I fix the error?

Upvotes: 5

Views: 8281

Answers (3)

ken
ken

Reputation: 2682

In case you are using Nestjs-query for graphql, which is this library

Check the library version, I used 5.0.0-alpha-1, this problem came out. The quick solution is just revert back to version-3.0.0, this problem is solved.

Upvotes: 0

Pradeep Nooney
Pradeep Nooney

Reputation: 820

code translation of @ Jay McDoniel explanation

typeorm.config.ts

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

export default class TypeOrmConfig {
  static getOrmConfig(configService: ConfigService): TypeOrmModuleOptions {
    return {
      type: 'postgres',
      host: configService.get('DB_HOST') || 'localhost',
      port: configService.get('DB_PORT') || 5432,
      username: configService.get('DB_USERNAME'),
      password: configService.get('DB_PASSWORD'),
      database: configService.get('DB_NAME'),
      entities: [__dirname + '/../**/*.entity{.ts,.js}'],
      synchronize:configService.get<boolean>('TYPEORM_SYNCHRONIZE') || false,
      logging: configService.get<LoggerOptions>('TYPEORM_LOGGING') || false
    };
  }
}

export const typeOrmConfigAsync: TypeOrmModuleAsyncOptions = {
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService): Promise<TypeOrmModuleOptions> => TypeOrmConfig.getOrmConfig(configService),
  inject: [ConfigService]
};

app.module.ts

import { LoginModule } from './login/login.module';
import * as redisStore from 'cache-manager-redis-store';
import { ServiceModule } from './service/service.module';
import { UserModule } from './user/user.module';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { RedisCacheModule } from './redis-cache/redis-cache.module';
import { typeOrmConfigAsync } from './config/typeorm.config';
import { ConfigModule } from '@nestjs/config';

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

Reference video Reference code

Upvotes: 5

Jay McDoniel
Jay McDoniel

Reputation: 70570

One of two things needs to happen:

1) You need to make your ConfigModule global by passing the isGlobal: true option to ConfigModule.forRoot(). If you do this, then you can remove the import in the TypeormModule.forRootAsync() (it's then a global module and can have it's providers used anywhere)

2) Make another module (MyConfigModule or something) that imports the ConfigModule with its configuration and exports the CofnigModule. Then you can change ConfigModule.forRoot() to MyConfigModule in the AppModule and you can change imports: [ConfigModule] to imports: [MyConfigModule] in the TypeormModule.forRootAsync() configuration.

Upvotes: 9

Related Questions