Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Env Variables in NestJS not visible in every module?

Tags:

nestjs

Im keeping my configuration in .env file when I develop my app.

This is my app.module.ts:

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRoot({
      autoLoadEntities: true,
      database: process.env.TYPEORM_DATABASE,
      host: process.env.TYPEORM_HOST,
      password: process.env.TYPEORM_PASSWORD,
      port: (process.env.TYPEORM_PORT as unknown) as number,
      type: 'postgres',
      username: process.env.TYPEORM_USERNAME,
    }),
    AuthModule,
    (...)
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

And typeorm use proper values from process.env.TYPEORM_... variables.

This is my auth.module.ts:

@Module({
  providers: [JwtStrategy, (...)],
  imports: [
    JwtModule.register({
      secret: process.env.JWT_SECRET,
      (...)
    }),
    (...)
  ],
  controllers: [AuthController],
})
export class AuthModule {}

And Im getting error from JwtModule, that secret can not be empty. Of course JWT_SECRET is set in .env file.

This is my jwt.strategy.ts:

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      secretOrKey: process.env.JWT_SECRET,
      (...)
    });
  }
  (...)
}

And here, process.env.JWT_SECRET is properly loaded.

I cant understand why my env vars are not available everywhere in my app.

like image 252
gib Avatar asked Nov 07 '25 09:11

gib


2 Answers

If I had to take a guess, you are calling dotenv's config() method too late. In Typescript, decorators are run on module import, so anything in @Module() is run almost immediately. However, anything that is in a function call of a class method will not be run until that function is called. I'd suggest, in your main.ts file, having these as the first two lines:

import { config } from 'dotenv';
config();

This way, any .env file is read and added to process.env before anything else gets a chance to run.

The other option, as you are using the ConfigModule provided by Nest, is to use an asynchronous registration process where you use either a factory or a class instead to provide the proper configurations. In your TypeOrm config, an async registration process could look like this:

TypeOrmModule.forRootAsync({
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    autoLoadEntities: true,
    database: config.get<string>('TYPEORM_DATABASE'),
    host: config.get<string>('TYPEORM_HOST'),
    password: config.get<string>('TYPEORM_PASSWORD'),
    port: config.get<number>('TYPEORM_PORT'),
    type: 'postgres',
    username: config.get<string>('TYPEORM_USERNAME'),
  })
})
like image 65
Jay McDoniel Avatar answered Nov 10 '25 00:11

Jay McDoniel


For JwtModule access to env variables you can use registerAsync, your code should be something like this:

JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        secret: config.get('JWT_SECRET_KEY'),
        signOptions: { expiresIn: '1h' },
      }),
    })
like image 42
Dmitry Solyannik Avatar answered Nov 10 '25 00:11

Dmitry Solyannik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!