nestjs env variable undefined before class definition - node.js

I am working on app written in nestjs and trying to implement WebSocket. One problem i am having is with use of env variables. In the gateway file where i define my WebSocket implementation we have need of using PORT from env before class is defined:
console.log(process.env.WSPORT) // undefined
setInterval(() => {
console.log((process.env.WSPORT) // after few logs its becoming accessible
}, 100)
#WebSocketGateway(process.env.WSPORT)
export class ExportFeedsGateway implements OnGatewayInit {
I tried to debug what is going on, and it seems to be related with when it is invoked as few moments later this variable becomes available for use.
app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '#nestjs/config';
import { ExportFeedsGateway } from './modules/exportFeeds/export-feeds.gateway';
#Module({
imports: [ConfigModule.forRoot({ expandVariables: true })],
controllers: [AppController],
providers: [AppService, ExportFeedsGateway],
})
export class AppModule {}
export-feeds.gateway.ts
import { OnGatewayInit, WebSocketGateway } from '#nestjs/websockets';
#WebSocketGateway(process.env.WSPORT)
export class ExportFeedsGateway implements OnGatewayInit {
...
}
how to modify this code to make sure that WSPORT is not undefined when its passed to WebSocketGateway decorator?
----- EDIT
I was hoping to utilise nestjs code to access this variable rather then use packages like dotenv
main.ts
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { WsAdapter } from '#nestjs/platform-ws';
const PORT = process.env.PORT;
async function bootstrap() {
const app = await NestFactory.create(AppModule, { cors: true });
app.useWebSocketAdapter(new WsAdapter(app));
await app.listen(PORT);
}
bootstrap();

you need to inject that env var in the shell, not via .env. Or use the dotenv by yourself as the first line at your main.ts

I did deeper research and the problem i was having is actually result of trying to use WebSocket implementation (gateway) as standalone import. So the answer and solution to the problem is to create module first
import { Module } from '#nestjs/common';
import { ExportFeedsGateway } from './export-feeds.gateway';
#Module({
providers: [ExportFeedsGateway],
exports: [ExportFeedsGateway],
})
export class ExportFeedsModule {}
This is a proper way of doing things as far as nestjs is concerned.
After this step module should be imported instead of gateway and env variables are accessible through process.env.something
#WebSocketGateway(Number(process.env.EXPORT_FEEDS_PORT))
export class ExportFeedsGateway implements OnGatewayInit {
...
}

Related

NestJS - How to register dynamic module provider multiple times using different configuration?

I have a knex module which is implemented like this:
import { DynamicModule, Module } from '#nestjs/common';
import { Knex, knex } from 'knex';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
export const KNEX_MODULE = 'KNEX_MODULE';
#Module({})
export class KnexModule {
static register(options: Knex.Config): DynamicModule {
return {
module: KnexModule,
providers: [
{
inject: [WINSTON_MODULE_PROVIDER],
provide: KNEX_MODULE,
useFactory: (logger: Logger) => {
logger.info('Creating new knex instance', {
context: KnexModule.name,
tags: ['instance', 'knex', 'create'],
});
return knex(options);
},
},
],
exports: [KNEX_MODULE],
};
}
}
My application requires access to multiple databases, I know I can do that by creating multiple knex instances. So I tried to register the module twice, passing different configurations. However, the module only registered once. The second register call seems to be reusing the existing object instead of creating a new knex instance.
What is the correct way to generate multiple providers, depending on the configuration passed? The closest thing I found is the forFeature functions in typeORM and Sequelize
I just found the solution. I was thinking the wrong way. I needed to register two providers to my Module. Not create two instances of my module. I solved it by adding one more parameter to my module, which is the provider token. Now it correctly creates the two providers.
import { DynamicModule, Module } from '#nestjs/common';
import { Knex, knex } from 'knex';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
export const KNEX_MODULE = 'KNEX_MODULE';
#Module({})
export class KnexModule {
static register(token: string, options: Knex.Config): DynamicModule {
return {
module: KnexModule,
providers: [
{
inject: [WINSTON_MODULE_PROVIDER],
provide: token,
useFactory: (logger: Logger) => {
logger.info('Creating new knex instance', {
context: KnexModule.name,
tags: ['instance', 'knex', 'create'],
});
return knex(options);
},
},
],
exports: [token],
};
}
}
And whenever I want to use it I register it like this:
#Module({
imports: [KnexModule.register(CatRepository.KNEX_TOKEN, knexConfigs)],
providers: [CatRepository, CatService],
controllers: [CatController],
exports: [CatService],
})
export class CatModule {}
Then in the repository I can inject the knex instance of the cats database.
#Injectable()
export class CatRepository implements Repository<Cat> {
// eslint-disable-next-line no-useless-constructor
public static KNEX_TOKEN = 'KNEX_CATS_TOKEN';
// eslint-disable-next-line no-useless-constructor
constructor(
#Inject(CatRepository.KNEX_TOKEN)
protected knex: Knex,
) {}
...
}

NestJS Unable to Resolve Dependencies

I'm getting this error when I run my NestJS app.
[Nest] 19139 - 03/01/2020, 2:10:01 PM [ExceptionHandler] Nest can't resolve dependencies of the AccountsService (AccountRepository, ?, HashPasswordService). Please make sure that the argument Object at index [1] is available in the AccountsModule context.
Potential solutions:
- If Object is a provider, is it part of the current AccountsModule?
- If Object is exported from a separate #Module, is that module imported within AccountsModule?
#Module({
imports: [ /* the Module containing Object */ ]
})
+1ms
I am a bit confused what it causing this. As far as I can tell, my code looks correct. Here is the definition for my AccountsService class:
import { Injectable, ConflictException, Logger, InternalServerErrorException, NotFoundException, Inject } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Account } from 'src/accounts/entities/account';
import { Repository, FindManyOptions, UpdateDateColumn } from 'typeorm';
import { CreateAccount } from 'src/accounts/dtos/create-account';
import { GetAccountsWithFilters } from 'src/accounts/dtos/get-accounts-with-filters';
import { UpdateAccountProfileInfo } from 'src/accounts/dtos/update-account-profile-info';
import { HashPasswordService } from '../hash-password/hash-password.service';
import { UpdateEmail } from 'src/accounts/dtos/update-email';
import { UpdatePhone } from 'src/accounts/dtos/update-phone';
import { AccountRepository } from 'src/accounts/repositories/account-repository';
/**
* AccountsService encapsulates all the actions that can be performed by an account.
*/
#Injectable()
export class AccountsService {
constructor(
#InjectRepository(AccountRepository) private accountRepository: AccountRepository,
private logger = new Logger("Accounts Service"),
#Inject(HashPasswordService)
private hashPasswordService: HashPasswordService,
) { }
// more code here
}
My Module looks like this.
import { Module } from '#nestjs/common';
import { AccountsService } from './services/accounts/accounts.service';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Account } from './entities/account';
import { AccountsController } from './controllers/accounts/accounts.controller';
import { AccountCleanerService } from './services/account-cleaner/account-cleaner.service';
import { AuthenticationService } from './services/authentication/authentication.service';
import { AuthenticationController } from './controllers/authentication/authentication.controller';
import { HashPasswordService } from './services/hash-password/hash-password.service';
import { JwtModule } from "#nestjs/jwt";
import { PassportModule } from "#nestjs/passport";
import { JwtStrategy } from './auth-strategies/jwt-strategy';
import { AccountRepository } from './repositories/account-repository';
#Module({
imports: [
TypeOrmModule.forFeature([
Account,
AccountRepository,
]),
JwtModule.register({
secret: "SOME_APP_SECRET",
signOptions: {
expiresIn: 3600
}
}),
PassportModule.register({
defaultStrategy: "jwt",
}),
],
controllers: [
AccountsController,
AuthenticationController,
],
providers: [
AccountRepository,
HashPasswordService,
AccountsService,
AccountCleanerService,
AuthenticationService,
JwtStrategy,
],
exports: [JwtStrategy, PassportModule],
})
export class AccountsModule { }
Lastly, here is the App Module:
import { Module } from '#nestjs/common';
import { AccountsModule } from './accounts/accounts.module';
import { TypeOrmModule } from "#nestjs/typeorm";
import {Account} from "./accounts/entities/account";
import { ConfigModule } from "#nestjs/config";
import account from "./../config/account";
import auth from "./../config/auth";
import database from "./../config/database";
import server from "./../config/server";
import { AccountRepository } from './accounts/repositories/account-repository';
#Module({
imports: [
AccountsModule,
ConfigModule.forRoot({
// make this module available globally
isGlobal: true,
// The configuration files.
load: [
account,
auth,
database,
server
],
}),
TypeOrmModule.forRoot({
type: "mongodb",
url: "my connection string here",
entities: []
}),
],
controllers: [],
providers: [],
})
export class AppModule { }
As you can see, I have clearly made the services available to the module. So, I am kind of confused why Nest is unable to resolve the dependencies. Additionally, there should not be any other module right now, aside from the App module.,, which is also provided above. Any ideas why NestJS is throwing this error?
Nest is having trouble resolving the dependency of Logger, and as it is not provided in the providers array, it won't be able to resolve it. You've got three options:
1) Move the private logger = new Logger('Account Service') to the body of the constructor
2) Move the private logger = new Logger('Account Service') to the third position and mark it as #Optional() so that Nest doesn't throw an error when the value is unknown.
3) Add Logger to the providers array of AccountModule and then use the this.logger.setContext() method to properly set the context
The built in Logger class is #Injectable() so it is possible to use it through DI, but you have to ensure that it is provided just as any other provider is in the NestJS ecosystem.

Dependency injection - app.get(UploadService) on INestApplication doesn't resolve its dependencies

I am trying to create external CLI, which uses my Nest context. I've made entry-point, which creates app with NestFactory.create. After accessing service with app.get, the service exists and works itself. The problem is, that it doesn't resolve any of its dependencies. There is no example on Docs and neither have I found anything related to this issue on Internet.
I am using the newest version of Nest.js on Node 10 as on 17.07.2019.
main.ts
import { NestFactory } from '#nestjs/core';
import { AppModule } from '../app.module';
import { UploadService } from 'src/api/upload/upload.service';
import { UploadModule } from 'src/api/upload/upload.module';
async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule);
await app.init();
const service: UploadService = app.select(UploadModule).get(UploadService);
console.log(service); // OK - returns UploadService instance
console.log(service.uploadModel); // X - returns `undefined`
console.log(service.configService); // X - returns `undefined`
}
bootstrap();
app.module.ts
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { ConfigModule } from './config/config.module';
import { ConfigService } from './config/config.service';
import { AwsModule } from './services/aws/aws.module';
import { UploadModule } from './api/upload/upload.module';
#Module({
imports: [
ConfigModule,
MongooseModule.forRootAsync({
useFactory: async (configService: ConfigService): Promise<object> => ({
uri: configService.data.database.mongo.uri,
useCreateIndex: true,
useNewUrlParser: true,
}),
inject: [ConfigService],
}),
AwsModule,
UploadModule,
],
})
export class AppModule {}
upload.module.ts
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { AwsModule } from '../../services/aws/aws.module';
import { UploadService } from './upload.service';
import { UploadSchema } from './upload.schema';
#Module({
imports: [MongooseModule.forFeature([{ name: 'Upload', schema: UploadSchema }]), AwsModule],
providers: [UploadService],
exports: [UploadService],
})
export class UploadModule {}
upload.service.ts
import { Injectable } from '#nestjs/common';
import { AwsService } from '../../services/aws/aws.service';
import { Upload } from './upload.schema';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
#Injectable()
export class UploadService {
constructor(
#InjectModel('Upload')
readonly uploadModel: Model<Upload>,
readonly awsService: AwsService,
) {}
}
Expected outputs of
console.log(service.uploadModel); // X - returns `undefined`
console.log(service.configService); // X - returns `undefined`
are model/service instances. Unfortunately they return both undefined, as the dependency-injection doesn't take a place.
I tried what You did, and in nestjs version: 7.6 it works as expected.
So my assumption is they fixed it in meantime.
I had the same issue. The docs are actually pretty clear about this: https://docs.nestjs.com/standalone-applications
But I actually run into the same problem and I dont think this an intended behaviour. I tried removing a dependency after another to see if something changes.
I had request scoped services injected. As long there was an "unresolvable dependency" (request-scoped ones) all injected dependencies were undefined. I'd expect a "dependency cannot be resolved" error but it just silently failed.
See also https://github.com/nestjs/nest/issues/4630
Make sure all your dependencies can be created successfully within the correct scope for standalone apps.
To get a service with for example request-scoped dependencies use module.resolve(Service) instead of get.

How do I include a request scoped provider which needs an injected request in via an imported module

We are building up a mono-repo of microservices, and want to have some shared libraries which we import into various services.
Right now I am trying to build up a shared module which will have a provider which needs access to the request. Here is an example:
import { Injectable, Scope, Inject } from '#nestjs/common'
import { REQUEST } from '#nestjs/core'
import { Request } from 'express'
import { APILogger } from '#freebird/logger'
import { APIGatewayProxyEvent, Context } from 'aws-lambda'
export interface IAPIGatewayRequest extends Request {
apiGateway?: {
event?: APIGatewayProxyEvent
context?: Context
}
}
#Injectable({ scope: Scope.REQUEST })
export class RequestLogger extends APILogger {
constructor(#Inject(REQUEST) request: IAPIGatewayRequest) {
if (!request.apiGateway || !request.apiGateway.event || !request.apiGateway.context) {
throw new Error(
'You are trying to use the API Gateway logger without having used the aws-serverless-express middleware',
)
}
super(request.apiGateway.event, request.apiGateway.context)
}
}
I have been trying to bundle this as a module like so:
import { Module } from '#nestjs/common'
import { RequestLogger } from './logger'
#Module({
providers: [RequestLogger],
exports: [RequestLogger],
})
export class LambdaModule {}
And then import it into the main service module like this:
import { Module } from '#nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { LambdaModule } from '#freebird/nest-lambda'
#Module({
imports: [LambdaModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
However, when I do this I get an error:
Nest can't resolve dependencies of the RequestLogger (?). Please make
sure that the argument at index [0] is available in the AppModule
context.
But when I pull the RequestLogger provider into the service module, and include it like this I get no errors:
import { Module } from '#nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { RequestLogger } from './logger'
#Module({
controllers: [AppController],
providers: [AppService, RequestLogger],
})
export class AppModule {}
I discovered the problem. In my case I had slightly different requirements between my library package and the service package. So different versions of nest were in play. This apparently causes conflicts.

Error : Nest cannot export component / module that is not a part of the currently processed module (DatabaseModule)

I'm programming a really simple app allowed make CRUD feature on a PostgreSQL DB using express.
When I run my program I get this error :
Error: Nest cannot export component / module that is not a part of the currently proccessed module (DatabaseModule). Please verify whether each exported unit is available in this particular context.
I really don't understand why... In the app.module.ts, I import DatabaseModule, which calls database provider (where is my postreSQL connection).
I'm new on typescript, and I'm lost : /
I only post the entry point of app, but I can send more if it's not enough and thank's for the help :
import { NestFactory } from '#nestjs/core';
import { ApplicationModule } from './src/app.module';
import * as express from 'express';
import 'ts-node/register';
async function bootstrap() {
let instance: any;
instance = express();
const app = NestFactory.create(ApplicationModule, instance);
app.then(instance => instance.listen(3000), () => console.log('Application is listening on port 3000'));
}
bootstrap();
This app.module.ts
import { Module } from '#nestjs/common';
import { MainController } from './controller/main.controller';
import { CatsModule } from './cats.module';
import { CatsController } from './controller/cats.controller';
import { OwnerModule } from './owner.module';
import { OwnerController } from './controller/owner.controller';
import {CatfoodController} from './controller/catfood.controller';
import { CatfoodModule } from './catfood.module';
import { DatabaseModule } from './database/database.module';
#Module({
controllers: [
MainController,
CatsController,
CatfoodController,
OwnerController,
],
modules: [
DatabaseModule,
CatsModule,
CatfoodModule,
OwnerModule,
],
})
export class ApplicationModule {}
And database.module :
import { Module } from '#nestjs/common';
import { databaseProviders } from './database.providers';
#Module({
components: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
Github : https://github.com/lukile/catProject
I think you need to put the modules in imports array, not modules array. Please double check. And do CatsModule and CatfoodModule have their corresponding controllers in their class (which they should)? You don't have to put every controllers in ApplicationModule.
It seems to me that you created a module, but you didn't configure them. When you create a module using a #Module, you need to create at least an empty controllers array inside the #Module.
import { Module } from "#nestjs/common";
#Module({
controllers: []
})
export class AppModule {
}

Resources