Reputation: 10083
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:[email protected]?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],
})
Upvotes: 110
Views: 188428
Reputation: 71
db.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
@Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: () => ({
type: 'postgres',
host: process.env['DB_HOST'],
port: +process.env['DB_PORT'],
username: process.env['DB_USER'],
password: process.env['DB_PASSWORD'],
database: process.env['DB_DATABASE'],
synchronize: true,
logging: false,
entities: [__dirname + '/../**/*.entity.{js,ts}'],
autoLoadEntities: true,
namingStrategy: new SnakeNamingStrategy(),
}),
}),
],
})
export class DatabaseModule {}
Ensure that the DatabaseModule, as well as any other module, is positioned below the config module. This allows environment variables to be loaded at the application level first. For the DatabaseModule, you can use the async functions provided by any ORM during configuration.
app.module.ts
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
envFilePath: [`.env.${process.env.NODE_ENV}`, '.env'],
}),
CacheModule.register({
isGlobal: true,
}),
DatabaseModule,
BidsModule,
UsersModule,
],})
Upvotes: 2
Reputation: 3458
If someone needs an answer on how to use .env vars inside a configuration.js file used when initializing ConfigModule here is a little workaround I found. Otherwise it was not possible to use this defined environment vars inside the configuration.js. The approach to handle this configuration in app.module.ts:
import { ConfigModule } from '@nestjs/config'
import { configuration } from '@testlib/config'
// ...
// hacky way to make it work
ConfigModule.forRoot(),
ConfigModule.forRoot({
load: [configuration],
isGlobal: true,
}),
The solution was to call ConfigModule.forRoot() twice: First to make env vars privatly available and second the real initialization with configuration.js this time with appropiatly filled env vars. Without this first step the env vars are not accessible.
Here is an example of used configuration.js:
import 'dotenv/config'
let configuration
const e = process.env
if (e.NODE_ENV && e.NODE_ENV === 'development') {
// local dev config
configuration = {
test: e.TEST,
uri: 'http://localhost:2001/api/'
}
} else {
// production config
configuration = {
test: e.TEST,
uri: e.URI
}
}
const func = () => configuration
export { func as configuration }
Upvotes: 0
Reputation: 11
One way to implement this is to generate a database module using the @nestjs/cli
(you can create it manually if you like), then in the imports array of the module, add this line of code
imports: [
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
uri: configService.get('MONGODB_URI'),
}),
inject: [ConfigService],
}),
],
where ConfigService
is imported from @nestjs/config
(You can install it if you don't have it in your project).
Ensure you add the MONGODB_URI
in your .env
file and you should be good to go.
Then you can use the DatabaseModule
in your other modules by adding it to the imports array like this:
imports: [
DatabaseModule,
DatabaseModule.forFeature([
{ name: AnyDocumentYouWantToRegister.name, schema: AnySchemaYouWantToRegisterToMongoose },
]),
]
I hope this helps someone.
Upvotes: 0
Reputation: 11
There is much simpler way, that described in documentation.
In main.ts
file you need to access config service:
const configService = app.get(ConfigService);
And after that get access to environment variable:
const port = configService.get('PORT');
Upvotes: 0
Reputation: 21
MongooseModule.forRootAsync({
useFactory: () => ({
uri: process.env.DB_CONNECTION
}),
}),
ConfigModule.forRoot({isGlobal: true,}),
Upvotes: 2
Reputation: 75
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 { }
Upvotes: 4
Reputation: 5972
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"
}
Upvotes: 89
Reputation: 4113
From Nestjs docs here - https://docs.nestjs.com/techniques/configuration
These steps worked for me with MySQL and TypeORM.
Install Nestjs config module - npm i --save @nestjs/config
. It relies on dotenv
Create a .env
file in your root folder and add your key/value pairs e.g. DATABASE_USER=myusername
Open app.module.ts and import the config module
import { ConfigModule } from '@nestjs/config';
app.module.ts
. I added it a the first import. It will load the contents of the .env file automatically. ConfigModule.forRoot(),
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.
Upvotes: 127
Reputation: 1062
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>('MONGODB_URI'),
}),
inject: [ConfigService],
});
Upvotes: 38
Reputation: 537
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
Upvotes: 24
Reputation: 2531
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
Upvotes: 12