'NestJS setup TypeOrm connection with .env and @nestjs/config

I'm trying to find the most legal way to set up NestJS database using .env file. That is I want to use @nestjs/config package for importing .env variables and use them in the TypeOrmModule.

It seems I need to use TypeOrmModule.forRootAsync.

I'm trying to do it like that:

// app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    TypeOrmModule.forRootAsync({
      useClass: TypeOrmConfigService,
    }),
    ...
  ],

})
export class AppModule {}

Then, there's TypeOrmConfigService:

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';

@Module({
  imports: [ConfigModule],
})
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'mysql',
      host: this.configService.get('DATABASE_HOST'),
      username: this.configService.get('DATABASE_USERNAME'),
      password: this.configService.get('DATABASE_PASSWORD'),
    };
  }
}

The last one is incorrect: Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context.

How to fix it? Or (the most preferred) is there anywhere an example of NestJs + TypeOrm + @nestjs/config + .env (outside of the repo, with DATABASE_PASSWORD) + config (I mean npm package config that processes config/development.yml, config/production.yml etc.)?

It seems that I'm looking for a very standard thing, the hello world, that should be a start of every NestJS project, but I found difficulties combining @nestjs/config and TypeOrm.

Upd. If I replace @Module with @Injectable, the error is exactly the same:

yarn run v1.22.4
$ NODE_ENV=development nodemon
[nodemon] 1.19.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: /home/kasheftin/work/pubngn4/nestjs-course-task-management/src/**/*
[nodemon] starting `ts-node -r tsconfig-paths/register src/main.ts`
[Nest] 25384   - 09/01/2020, 8:07 PM   [NestFactory] Starting Nest application...
[Nest] 25384   - 09/01/2020, 8:07 PM   [InstanceLoader] AppModule dependencies initialized +11ms
[Nest] 25384   - 09/01/2020, 8:07 PM   [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 25384   - 09/01/2020, 8:07 PM   [InstanceLoader] PassportModule dependencies initialized +0ms
[Nest] 25384   - 09/01/2020, 8:07 PM   [ExceptionHandler] Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context. +1ms
Error: Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context.
    at Injector.lookupComponentInExports (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:180:19)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at Injector.resolveComponentInstance (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:143:33)
    at resolveParam (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:96:38)
    at async Promise.all (index 0)
    at Injector.resolveConstructorParams (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:112:27)
    at Injector.loadInstance (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:78:9)
    at Injector.loadProvider (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:35:9)
    at async Promise.all (index 3)
    at InstanceLoader.createInstancesOfProviders (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/instance-loader.js:41:9)
 1: 0xa2afd0 node::Abort() [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 2: 0xa9e7a9  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 3: 0xc06bab  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 4: 0xc08156  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 5: 0xc087d6 v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 6: 0x13a9f19  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
Aborted (core dumped)
[nodemon] app crashed - waiting for file changes before starting...


Solution 1:[1]

It's my working script with Postgres in app.module.ts, but for MySQL will be very similar. And sometimes before rebuild need to remove dist folder manually - when DB can't synchronize.

import { ConfigModule, ConfigService } from '@nestjs/config';


@Module({
  imports: [
    ConfigModule.forRoot(),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],      
      useFactory: (configService: ConfigService) => ({
        type: 'postgres' as 'postgres',
        host: configService.get<string>('DATABASE_HOST'),
        port: parseInt(configService.get<string>('DATABASE_PORT')),
        username: configService.get<string>('DATABASE_USER'),
        password: configService.get<string>('DATABASE_PASS'),
        database: configService.get<string>('DATABASE_NAME'),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: true,
      }),
      inject: [ConfigService],
    }),

.env in root folder

DATABASE_USER=
DATABASE_PASS=
DATABASE_HOST=
DATABASE_NAME=
DATABASE_PORT=

Solution 2:[2]

on database.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        type: 'mysql',
        host: configService.get('DATABASE_HOST'),
        port: configService.get('DATABASE_PORT'),
        username: configService.get('DATABASE_USERNAME'),
        password: configService.get('DATABASE_PASSWORD'),
        database: configService.get('DATABASE_NAME'),
        entities: ['dist/**/*.entity.js'],
        synchronize: true,
      }),
    }),
  ],
})
export class DatabaseModule {}

on app.module.ts

import { DatabaseModule } from './database/database.module';
import { ConfigModule } from '@nestjs/config';
import { Module } from '@nestjs/common';

@Module({
  imports: [
    ConfigModule.forRoot({}),
    DatabaseModule,
  ],
})
export class AppModule {}

This works for me

Solution 3:[3]

Not sure if it is still relevant, however, I think this is whats going on.

The error that you see "Nest cant't resolve..." means that you are trying to inject a class using dependency injection, however Nest can't find this dependency in the dependencies container.

In order to resolve the issue, you need to add ConfigService (the failed to resolve dependency) to app.module.ts Providers section.

Something like that:

@Module({
   imports: [
        ConfigModule.forRoot(),
        TypeOrmModule.forRoot({
          type: 'mysql',
          host: process.env.DATABASE_HOST,
          username: process.env.DATABASE_USERNAME,
          password: process.env.DATABASE_PASSWORD,
        }),
        ...
   ],
   providers: [
        ConfigService
   ]
    
})
export class AppModule {}

Solution 4:[4]

you can try this:

// app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot(),
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: process.env.DATABASE_HOST,
      username: process.env.DATABASE_USERNAME,
      password: process.env.DATABASE_PASSWORD,
    }),
    ...
  ],

})
export class AppModule {}

Solution 5:[5]

You can use "nestjs-easyconfig", i use it for multi .env (e.g: .env.development, .env.production' it's almost same with nestjs/config. you can check my github repo

Solution 6:[6]

Your TypeOrmConfigService should not be a @Module(). It should be @Injectable(). Everything else looks fine.

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';

@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'mysql',
      host: this.configService.get('DATABASE_HOST'),
      username: this.configService.get('DATABASE_USERNAME'),
      password: this.configService.get('DATABASE_PASSWORD'),
    };
  }
}

Solution 7:[7]

Create a file (in this case the file is database.provider.ts) the file will export an array of connections.

import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
export const databaseProviders = [
  TypeOrmModule.forRootAsync({
    imports: [ConfigModule],
    inject: [ConfigService],
    useFactory: (configService: ConfigService) => ({
      type: 'postgres',
      host: configService.get('PGHOST'),
      port: +configService.get<number>('PGPORT'),
      username: configService.get('PGUSER'),
      password: configService.get('PGPASSWORD'),
      database: configService.get('PGDATABASE'),
      entities: ['dist/**/*.entity{.ts,.js}'],
      synchronize: false,
      logging: true,
    }),
  }),
];

Then import it in your database.module.ts

import { databaseProviders } from './database.provider';
import { DatabaseService } from './database.service';
import { Module } from '@nestjs/common';
@Module({
  imports: [...databaseProviders],
  providers: [DatabaseService],
  exports: [...databaseProviders],
})
export class DatabaseModule {}

And that's all, you can add multiple connections in your database.provider.ts if you want it, also, donĀ“t forget to create the .env and import the database module in your root module.

Solution 8:[8]

you can spit it to another module typeorm.module.ts

import { ConfigService } from '@nestjs/config';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useFactory: async (config: ConfigService) => ({
        database: config.get<string>('DATABASE'),
        username: config.get<string>('USERNAME'),
        password: config.get<string>('PASSWORD'),
        host: config.get<string>('HOST'),
        port: parseInt(config.get('PORT')),
        autoLoadEntities: true,
        synchronize: true,
        type: 'postgres',
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [],
  providers: [],
})
export class TypeormConfigModule {}

main app.module.ts import like this*

import { TypeormConfigModule } from './config/database/typeorm.module';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';


@Module({
  imports: [
    CaslModule,
    TypeormConfigModule,
    ConfigModule.forRoot({
      envFilePath: ['env/dev.env', 'env/prod.env', 'env/rom.env'],
      isGlobal: true,
    }),
  
  ],
})
export class AppModule {}


** env is folder name and prod.env is env file or you can put it directly at app.module.ts as above**

ANOTHER METHOD TO USE ENV TOGETHER IN APP.MODULES.TS DIRECTLY

@Module({
  imports: [
    CaslModule,
   TypeOrmModule.forRootAsync({
      useFactory: async (config: ConfigService) => ({
        database: config.get<string>('DATABASE'),
        username: config.get<string>('USERNAME'),
        password: config.get<string>('PASSWORD'),
        host: config.get<string>('HOST'),
        port: parseInt(config.get('PORT')),
        autoLoadEntities: true,
        synchronize: true,
        type: 'postgres',
      }),
      inject: [ConfigService],
    }),
]
}),
    ConfigModule.forRoot({
      envFilePath: ['env/dev.env', 'env/prod.env', 'env/rom.env'],
      isGlobal: true,
    }),
  
  ],
})
export class AppModule {}

Solution 9:[9]

for me I just add this as my example

import { Injectable } from '@nestjs/common';
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from '@nestjs/typeorm';
import{Config} from'./config'
import { ConfigService } from '@nestjs/config';

@Injectable()
export class DatabaseConnectionService implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      name: 'default',
      type: 'postgres',
      host: this.configService.get<string>("DATABASE_HOST"),
      port: Number(this.configService.get<number>("DATABASE_PORT")) ,
      username: this.configService.get<string>("DATABASE_USERNAME"),
      password: this.configService.get<string>("DATABASE_PASSWORD"),
      database: this.configService.get<string>("DATABASE_DB"),
      synchronize: true,
      sslmode:"disable",
      logging: true,  
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
    
      migrations: [__dirname + "/migrations/**/*{.ts,.js}"],
      migrationsRun: false,
      maxQueryExecutionTime: 0.1 /** To log request runtime */,
      cli: {
        migrationsDir: __dirname + "/migrations/**/*{.ts,.js}",
      },
      
      
    }
  }
}

in the app.module.ts just add this code in import section

imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),

    TypeOrmModule.forRootAsync({
      useClass: DatabaseConnectionService,
    })]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 V.Tur
Solution 2 abbbel
Solution 3 yariv_kohn
Solution 4 Hadas
Solution 5 Denny Danu
Solution 6
Solution 7 Juan Rambal
Solution 8 BlueDragon
Solution 9 Mohammed Al-Reai