Carven
Carven

Reputation: 15638

NestJs: Unable to read env variables in module files but able in service files?

I have an .env file at the root of my NestJs project with some env variables in it.

The strange thing is that I am able to read the variables in service files but not in module files.

So in a service file like users.service.ts, this works:

saveAvatar() {
    const path = process.env.AVATAR_PATH    // returns value from .env
}

However, when accessing a path in a module file like auth.module.ts, this returns an empty value:

@Module({
    imports: [
       JwtModule.register({
          secretOrPrivateKey: process.env.SECRET   // process.env.SECRET returns an empty string
       })
    ]
})

Why is that so? How can I reliably access environmental variables in the .env file in NestJs?

Upvotes: 37

Views: 59373

Answers (13)

Rahul Verma
Rahul Verma

Reputation: 276

Check TypeScript Configuration: If you're using TypeScript, make sure you have "esModuleInterop": true and "allowSyntheticDefaultImports": true set in your tsconfig.json file. This allows you to import default exports from modules that don't explicitly define them.

Upvotes: 1

Trevodorax
Trevodorax

Reputation: 671

I had a similar issue, and solved by providing the JWT secret directly in the service, in the signAsync function, instead of trying to load the module dynamically.

Saves quite a lot of headaches.

Upvotes: 0

An Chi
An Chi

Reputation: 21

I just use the import 'dotenv/config'; only in the auth.module.ts that solved my problem. I think the JwtModule is initialized before the ConfigModule (I see the log messages when start application), so it can not access the process.env.

Upvotes: 2

Henry
Henry

Reputation: 1337

I solved the same issue by changing the import order: move 'dotenv/config' ahead of AppModule, as the following:

import 'dotenv/config'
import { AppModule } from './app.module';

Upvotes: 7

Anurag Yadav
Anurag Yadav

Reputation: 71

I was having same problem, just configure your ConfigModule as mentioned in NestJs Documentation but one more thing you have to add envFilePath:'.env' in your app.module.ts as Below

@Module({
  imports: [
  ConfigModule.forRoot({
    isGlobal: true,
    envFilePath:'.env',
  })
]

And to use .env variables you can use process.env.VARIABLE_NAME, if it is not working for you, then you have to use configService.get('') method as below code

constructor(private readonly configService: ConfigService) {
        const secretKey =this.configService.get('STRIPE_SECRET_KEY');
        }

and make sure to save .env file in the root folder outside src.

Upvotes: 2

Misfi
Misfi

Reputation: 1

Assuming you're using Jetbrains IDE:

You may need to remove the baseUrl from your tsconfig.json. There is a ticket related to this behavior. Removing 'baseUrl' is fine as long as you use paths instead, according to docs (TS version >= 4.1).

Upvotes: 0

Craig
Craig

Reputation: 19

I had a similar issue. Had written the code on another machine in which it ran fine. Cloned the repo onto another machine tried to run but hit the the env variables issue because I didn't have the .env file. Copied it across but still got the error message. Removing node_modules and installing followed by nom run build solved the issue for me

Upvotes: 1

JALF
JALF

Reputation: 140

it is works for me

@Module({
    imports: [ConfigModule.forRoot({
        envFilePath: join(process.cwd(), 'env', `.env.${process.env.SCOPE.trim()}`),
    })]
})

The solution is to use the trim() function because in Windows spaces are added to the end of the value of the environment variable

Upvotes: 0

William Vazquez
William Vazquez

Reputation: 129

I just realize that I solve my problem also, just importing dotenv and calling the method config at the beginning of my module (auth.module.ts).

import dotenv from "dotenv";

dotenv.config({path:<path-to-env-file>})

No need to specify the path if you are using the default .env file at the root of the project.

Upvotes: 7

Kim Kern
Kim Kern

Reputation: 60357

Your .env file is not yet read in when your JwtModule is instantiated. So either read it in earlier e.g. in your main.ts before the nest app is created or better: create a ConfigService and make the dependency on your config explicit:

JwtModule.registerAsync({
    imports: [ConfigModule],
    useFactory: async (configService: ConfigService) => ({
      secret: configService.jwtSecret,
    }),
    inject: [ConfigService],
}),

See this answer on how to create a ConfigService.

Upvotes: 50

hugo blanc
hugo blanc

Reputation: 328

The declaring order is important in your use case.

This works:

@Module({
  imports: [
    ConfigModule.forRoot(),
    ScheduleModule.forRoot(),
    TypeOrmModule.forRoot({
      type: 'mongodb',
      host: process.env.SYN_MONGO_HOST,
      port: +process.env.SYN_MONGO_PORT,
      username: process.env.SYN_MONGO_USERNAME,
      password: process.env.SYN_MONGO_PASSWORD,
      database: process.env.SYN_MONGO_DATABASE,
      authSource: 'admin',
      autoLoadEntities: true,
    }),
  ],
  controllers: [],
  providers: [],
})
export class ConfigurationModule {}

When this doesn't

@Module({
  imports: [
    ScheduleModule.forRoot(),
    TypeOrmModule.forRoot({
      type: 'mongodb',
      host: process.env.SYN_MONGO_HOST,
      port: +process.env.SYN_MONGO_PORT,
      username: process.env.SYN_MONGO_USERNAME,
      password: process.env.SYN_MONGO_PASSWORD,
      database: process.env.SYN_MONGO_DATABASE,
      authSource: 'admin',
      autoLoadEntities: true,
    }),
    ConfigModule.forRoot(),
  ],
  controllers: [],
  providers: [],
})
export class ConfigurationModule {}

This is because ConfigModule is load before or after TypeOrmModule.

Upvotes: 10

William Vazquez
William Vazquez

Reputation: 129

Like @KimKen said, the problem is that, by the time that the JwtModule is instantiated the environment variables still are not loaded. However, I have a different approach to @KimKen's answer that you might also be interested in.

First of all NestJS provide a ConfigModule that load the environment variables, so you don't need to create one, unless you wish handle it different than the usual. (https://docs.nestjs.com/techniques/configuration)

Now, to solve the problem I made the module (auth.module.ts) dynamic. In short words a dynamic module is module that receive parameters, and it depend on that input parameters for its right instantiation.(https://docs.nestjs.com/fundamentals/dynamic-modules)

The real thing going on here, is that the JwtModule is also a dynamic module because it depend on a variable for its right instantiation. So, that cause that also your module depend on parameters for its right instantiation, so make it dynamic! :).

Then your auth.module will be something like:

@Module({})
export class AuthModule {

    static forRoot(): DynamicModule {
        return {
            imports: [
                JwtModule.register({
                    secretOrPrivateKey: process.env.SECRET   // process.env.SECRET will return the proper value
                })
            ],
            module: AuthModule
        }
    }

Then it will be as easy as in your app.module or where ever you load the auth.module import it through the forRoot static method.

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

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

Note: I recommend import ConfigModule once in the app.module.ts

PD: You could make the dynamic auth.module receive a parameter in the forRoot method and in the app.module pass the environment variable process.env.SECRET

AuthModule.forRoot(process.env.SECRET)

but it seems that dynamic modules are loaded last so there is not need for that.

Upvotes: 5

Kamuran S&#246;necek
Kamuran S&#246;necek

Reputation: 3333

You can use it as global for access from other modules.

When you want to use ConfigModule in other modules, you'll need to import it (as is standard with any Nest module). Alternatively, declare it as a global module by setting the options object's isGlobal property to true, as shown below. In that case, you will not need to import ConfigModule in other modules once it's been loaded in the root module (e.g., AppModule)

.https://docs.nestjs.com/techniques/configuration#use-module-globally

ConfigModule.forRoot({
  isGlobal: true
});

Also you can find here how to use config service: https://docs.nestjs.com/techniques/configuration#using-the-configservice

Upvotes: 5

Related Questions