87

I am working on my first NestJS application, which was working fine with hardcoded database connecting string in app.module.ts.

But then as per our requirements, I had to pick the database config values from environment files. For that, I followed the configuration documentation on the nestjs documentation website - https://docs.nestjs.com/techniques/configuration

But the issue is that I need to use the .env variables inside the same file for database connection, which is failing.

Here is my original code that was working fine:

@Module({
  imports: [
    MongooseModule.forRoot(`mongodb+srv://myusername:mypassword@myhost.net?retryWrites=true&w=majority&db=dbname`, { useNewUrlParser: true, dbName: 'dbname' }),
    ProductModule,
    CategoryModule,
  ],
  controllers: [
    AppController,
    HealthCheckController,
  ],
  providers: [AppService, CustomLogger],
})

Now, I wanted to pick those DB values from .env files which are like local.env, dev.env etc. depending on the environment. Now, my this code is not working:

@Module({
  imports: [
    ConfigModule.forRoot({ envFilePath: `${process.env.NODE_ENV}.env` }),
    MongooseModule.forRoot(`mongodb+srv://${ConfigModule.get('DB_USER')}:${ConfigModule.get('DB_PASS')}@myhost.net?retryWrites=true&w=majority&db=dbname`, { useNewUrlParser: true, dbName: 'dbname' }),
    ProductModule,
    CategoryModule,
  ],
  controllers: [
    AppController,
    HealthCheckController,
  ],
  providers: [AppService, CustomLogger],
})
Ravi Garg
  • 1,378
  • 12
  • 23
Mohit Bhardwaj
  • 9,650
  • 3
  • 37
  • 64

6 Answers6

97

From Nestjs docs here - https://docs.nestjs.com/techniques/configuration

These steps worked for me with MySQL and TypeORM.

  1. Install Nestjs config module - npm i --save @nestjs/config. It relies on dotenv

  2. Create a .env file in your root folder and add your key/value pairs e.g. DATABASE_USER=myusername

  3. Open app.module.ts and import the config module

    import { ConfigModule } from '@nestjs/config';
  1. Add below line to the imports section of app.module.ts. I added it a the first import. It will load the contents of the .env file automatically.
    ConfigModule.forRoot(),
  1. Then you can begin to use the env variables as per the usual process.env.<variable_name> in the database config section e.g.
    process.env.DATABASE_USER

For more configuration of the ConfigModule, see the link above. You can use a custom file/path and set the module visible globally.

lloiacono
  • 4,714
  • 2
  • 30
  • 46
swedge218
  • 3,593
  • 1
  • 14
  • 13
  • 1
    I tried to do this with the "@nestjs/dotenv" module but it did not work. Worked perfectly with "@nestjs/config". Thanks! – José Manuel Blasco Oct 22 '21 at 09:46
  • 2
    This IS what Nest.js documents recommend, and the paid Nest.js course. However, as of mid 2022, this is not working. I followed the steps in the documentation and course exactly, and I'm getting "client password must be a string". I'm getting ConfigModule dependencies and then that error is thrown. – Michael Jay Jun 08 '22 at 23:49
  • you can also juste call ConfigModule.forRoot() as a normal function outside import and ask for process.env data, thanks – Alexy Jun 30 '22 at 14:02
  • @MichaelJay I ran into the same issue and I had to use the async initialization function: ```JwtModule.registerAsync({ useFactory: async () => ({ secretOrPrivateKey: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: process.env.JWT_EXPIRATION_TIME, }, }), }),``` – Rami Jan 21 '23 at 18:21
74

1. Keeping using ConfigModule

You need to set NODE_ENV in npm scripts so that it can be used to load an env file based on the env.

"scripts": {
  "start:local": "NODE_ENV=local npm run start"
  "start:dev": "NODE_ENV=dev npm run start"
}

Now you can just use the ConfigModule:

@Module({
  imports: [
    ConfigModule.forRoot({ envFilePath: `${process.env.NODE_ENV}.env` }), 
MongooseModule.forRoot(`mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}@myhost.net?retryWrites=true&w=majority&db=dbname`, { useNewUrlParser: true, dbName: 'dbname' })
    ...
})

2. Using dotenv

npm install dotenv

Add some scripts to your package.json to set what env you are in.

"scripts": {
  ...
  "start:local": "NODE_ENV=local npm run start"
  "start:dev": "NODE_ENV=dev npm run start"
}

Import dotenv in main.ts file. Make sure you do it at the top of the file.

require('dotenv').config({ path: `../${process.env.NODE_ENV}.env` });

3. Using env-cmd

You can use env-cmd npm package.

npm install env-cmd

And add some commands for different envs in package.json, for example:

"scripts": {
  ...
  "start:local": "env-cmd -f local.env npm run start"
  "start:dev": "env-cmd -f dev.env npm run start"
}
...

Now you can use the env variables, for example:

MongooseModule.forRoot(`mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}@myhost.net?retryWrites=true&w=majority&db=dbname`, { useNewUrlParser: true, dbName: 'dbname' })

process.env.MONGO_CONNECTION_STRING

Update:

To overcome the env set command problem in different platforms, you can install cross-env package.

npm install -D cross-env

And update the scripts:

"scripts": {
  "start:local": "cross-env NODE_ENV=local npm run start"
  "start:dev": "cross-env NODE_ENV=dev npm run start"
}
critrange
  • 5,652
  • 2
  • 16
  • 47
  • 4
    Thanks. I tried your first suggestion and it worked fine for me (the only thing was that the require('dotenv') line had to come before the import of AppModule). This solution is simple and quick :) – Mohit Bhardwaj Aug 06 '20 at 15:05
  • can you show me the main.ts and app module where you wrote this working code ? – Anmol Jain Mar 23 '21 at 05:55
  • 1
    you can just put the `require('dotenv').config({ path: `../${process.env.NODE_ENV}.env` });` at the top of your main.ts. – critrange Mar 23 '21 at 10:45
  • 4
    Nest config service already has 'dotenv' under the hood – kosiakMD Apr 27 '21 at 23:33
  • 1
    I am facing an issue with this method then env file is not loading although NODE_ENV is set it can't read the env – Shashank Dubey Oct 06 '21 at 11:22
  • I've got this error `'NODE_ENV' is not recognized as an internal or external command,` the script: ``` "start": "NODE_ENV=dev nest start", "start:dev": "NODE_ENV=dev nest start --watch", ``` – AhmadDeel Jun 11 '22 at 04:10
  • Hi @AhmadDeel that's because the command was for linux. you may need cross-env package and use `cross-env NODE_ENV=dev nest start` – critrange Jun 13 '22 at 23:58
  • I'm using `SET NODE_ENV=prod&& node dist/main` in windows now. somehow there should be no space between `prod` and `&&` – AhmadDeel Jun 28 '22 at 12:21
30
MongooseModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    uri: configService.get<string>('MONGODB_URI'),
  }),
  inject: [ConfigService],
});
kosiakMD
  • 923
  • 7
  • 22
22

By using nestjs/config package:

npm install @nestjs/config

After installing the package, in the app module (app.module.ts file):

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ConfigModule.forRoot()],
})

export class AppModule {}

After that .env files can be accessed all over the app. Suppose your .env file looks like this.

DB_USER=mohit

to access DB_USER variable use process.env.DB_USER

Raiyad Raad
  • 482
  • 5
  • 17
  • 2
    This does the job to get environment variables in runtime, but OP also wants to get environment variables while importing the module. With your solution, OP needs to load mongoose module asynchronously, as @Daniel pointed out. – buriedalive Apr 24 '21 at 14:29
  • 1
    Tried this. This does not load .env variables. And process.env can be used without even using ConfigModule – A.W. Sep 04 '21 at 15:28
  • FYI - verify your .env file uses EQUALS and not COLON. This is right`DB_USER=mohit` and this is wrong `DB_USER:mohit`. I did this twice already :D – Andy Danger Gagne Apr 15 '22 at 14:46
11

You need to use the MongooseModule.forRootAsync(() => {...}) instead of MongooseModule.forRoot(...)

This makes MongooseModule wait for its IOC dependencies.

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

Daniel
  • 2,288
  • 1
  • 14
  • 22
  • 2
    I updated my code as you suggested. But now I'm getting error - `Nest cannot export a module that is not part of the currently processed module`. Here is what I updated - `MongooseModule.forRootAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ uri: 'mongodb+srv://db_username:db_pass@myhost.net?retryWrites=true&w=majority', options: { useNewUrlParser: true, dbName: 'dbname' }, }), inject: [ConfigModule], }),` – Mohit Bhardwaj Aug 06 '20 at 14:43
  • 2
    fix the inject from `ConfigModule` to `ConfigService` (Follow the NestJS docs) – Daniel Aug 09 '20 at 11:21
1

To use NestJS configuration (dot-env) in the app.module.ts itself, use MongooseModule.forRootAsync({}). Here is my code.

  @Module({
  imports: [
    MongooseModule.forRootAsync({
      useFactory: () => ({
        uri: process.env.CONNECTION_STRING,
      }),
    }),
    ConfigModule.forRoot(
      {
        isGlobal: true
      }
    )
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }
Sarnava
  • 13
  • 4
  • it still gives me the same error. – hoho hotstuff Jan 24 '23 at 16:00
  • One thing that I personally do, in my projects is, create a file env.ts, and export configuration from that file. Example:- `export const port = 8080`, then import it `import { port } from 'env'`. This should work without `forRootAsync` and should work with `forRoot` too. I haven't tried it. – Sarnava Jan 29 '23 at 05:31