I've got two UseCases named, FirstUseCase and SecondUseCase.
FirstUseCases
export class FirstUsecases {
constructor(
private readonly something: Something
) {}
async execute(): Promise<any> {
console.log("This is FirstUsecases class.");
}
}
SecondUseCases
export class SecondUsecases {
constructor(
// How to inject FirstUsecases here?
private readonly firstUseCases: FirstUsecases ) {}
async execute(): Promise<any> {
this.firstUseCases.execute();
}
}
I wanna inject FirstUsecases into the SecondUsecases class.
Note: the FirstUsecases and SecondUsecases are in the same module named MyProxyModule.
MyProxyModule
static register(): DynamicModule {
return {
module: MyProxyModule,
providers: [
{
inject: [FirstRepository],
provide: "FIRST_USECASE",
useFactory: (firstRepository: FirstRepository) => new FirstUsecases(firstRepository),
},
{
inject: [SecondRepository],
provide: "SECOND_USECASE",
useFactory: (secondRepository: SecondRepository) => new SecondUsecases(secondRepository),
},,
]
}
}
How can I inject FirstUsecases into SecondUsecases?
If you're using new like that, you're in charge of passing the dependencies the class needs. To get an instance of the FirstUsecases you can add its injection token to the inject array, like how you added SecondRepository and then pass it on to the constructor.
static register(): DynamicModule {
return {
module: MyProxyModule,
providers: [
{
inject: [FirstRepository],
provide: "FIRST_USECASE",
useFactory: (firstRepository: FirstRepository) => new FirstUsecases(firstRepository),
},
{
inject: [SecondRepository "FIRST_USECASE"],
provide: "SECOND_USECASE",
useFactory: (secondRepository: SecondRepository, firstUsecase: FirstUsercase) => new SecondUsecases(secondRepository, firstUsecase),
},,
]
}
}
Related
I am trying to use jsonwebtoken with NestJs, and have been battling with this issue whereby NestJS returns an empty object once it encouters my code line to generate a token.
Each time, I try to generate a token, the server simply responds with an empty object.
Here is my jwt strategy code for passport
import { ConfigService } from "#nestjs/config";
import { ExtractJwt, Strategy } from "passport-jwt";
import { PassportStrategy } from "#nestjs/passport";
import { Inject, Injectable, UnauthorizedException } from "#nestjs/common";
import { RidersService } from "../riders/riders.service";
import { DriversService } from "../drivers/drivers.service";
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly riderService: RidersService,
private readonly driverService: DriversService,
#Inject(ConfigService) config: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: config.get("JWT_SECRET"),
});
}
async validate(payload: {
role: string;
iat: number;
exp: number;
phone: string;
}) {
if (!payload) {
throw new UnauthorizedException();
}
const service = {
rider: this.riderService,
driver: this.driverService,
};
const user = await service[payload.role].findByAny(payload.phone);
if (!user) {
throw new UnauthorizedException();
}
// eslint-disable-next-line #typescript-eslint/no-unused-vars
const { iat, exp, ...rest } = payload;
return rest;
}
}
Now here is my AuthService stripped off
import { InjectModel } from "#nestjs/mongoose";
import { Injectable, Logger, HttpException } from "#nestjs/common";
import { JwtService } from "#nestjs/jwt";
#Injectable()
export class AuthService {
private readonly logger = new Logger(AuthService.name);
constructor(
private readonly jwtService: JwtService,
) {}
async register(
phone: string,
role: string,
): Promise<LoginResponseDTO | GENERIC_RESPONSE | unknown> {
return {
token: this.jwtService.sign({ role, phone }),
};
}
}
This line return { token: this.jwtService.sign({ role, phone }), } returns an empt object {} no matter what I do, once the code execution gets there, it stops and sends back an empty response.
Here is my Authmodule stripped off
import { JwtStrategy } from "./jwt.strategy";
import { Module } from "#nestjs/common";
import { AuthService } from "./auth.service";
import { AuthController } from "./auth.controller";
import { PassportModule } from "#nestjs/passport";
import { JwtModule } from "#nestjs/jwt";
import { ConfigModule, ConfigService } from "#nestjs/config";
#Module({
imports: [
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>("JWT_SECRET"),
signOptions: { expiresIn: configService.get<string>("JWT_EXPIRESIN") },
}),
inject: [ConfigService],
}),
],
providers: [
AuthService,
JwtStrategy,
],
exports: [AuthService],
controllers: [AuthController],
})
export class AuthModule {}
Here is my Authcontroller stripped off
import { AuthService } from "./auth.service";
import { BadRequestException, Controller, Post, Query } from "#nestjs/common";
import { ParsePhonePipe } from "./../pipes/transform-phone.pipes";
#Controller("auth")
export class AuthController {
constructor(private readonly authService: AuthService) {}
#Post("signup")
async register(
#Query("phone", new ParsePhonePipe()) phone: string,
#Query("role") role: string,
) {
if (!role) {
throw new BadRequestException("user role is missing");
}
try {
const response = await this.authService.register(phone, role);
console.log(response, "RESPONSE");
return response;
} catch (error) {
return error;
}
}
}
Here is Appmodule stripped of
import { ConfigModule, ConfigService } from "#nestjs/config";
import { Module } from "#nestjs/common";
import { APP_GUARD } from "#nestjs/core";
import { AuthModule } from "./auth/auth.module";
import { JwtAuthGuard } from "./auth/jwt.auth.guard";
#Module({
imports: [
AuthModule,
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>("RYDR_DB_URI"),
}),
inject: [ConfigService],
}),
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
TripHistoryService,
DriversService,
],
})
export class AppModule {}
The login response has nothing to do with the jwt as there are three possible response types defined.
I am unable to generate jwt token and it simply exit with an empty object {}, there is no error whatsoever.
What might I be doing wrong?
I tried to use jsonwebtoken package itself, expecting that there might be an issue with NestJs but it still failed to generate any token instead it returned an empty object as a response.
There are plenty of articles showing how to inject providers into dynamic module but all of them only show examples using their exported services, none with middleware. Using the exact same method as used with services for middleware fails. How can I create reusable middleware that requires providers to be injected?
Example middleware, requiring injection of a UserService provider
#Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(#Inject(USER_SERVICE) private readonly userService: UserService) {
console.log('userService findAll: ', userService.findAll());
}
use(req: any, res: any, next: () => void) {
console.log('AuthMiddleware called!');
next();
}
}
Example module containing the middleware:
#Module({
providers: [AuthService, AuthMiddleware],
imports: [ExtModule],
})
export class AuthModule extends createConfigurableDynamicRootModule<
AuthModule,
AuthModuleOptions
>(AUTH_OPTIONS, {
providers: [
{
provide: AUTH_SECRET,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.secret,
},
{
provide: USER_SERVICE,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.userService,
},
],
controllers: [AuthController],
}) {}
Now importing the module and trying to use the middleware:
#Module({
imports: [
ConfigModule.forRoot(),
AuthModule.forRootAsync(AuthModule, {
imports: [ConfigModule, UserModule],
inject: [ConfigService, UserService],
useFactory: (config: ConfigService, userService: UserService) => {
return {
secret: config.get('AUTH_SECRET_VALUE'),
userService,
};
},
}) as DynamicModule,
UserModule,
],
controllers: [UserController],
providers: [],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
}
Now when initializing the AuthModule dependencies the middleware class is clearly instantiated correctly, the userService.findAll being logged:
userService findAll: []
So far so good, and this works fine for services.
But the problem is that when then actually using the middleware in configure(), the injected context is not used
...\app\node_modules\#nestjs\core\injector\injector.js:193
throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
^
Error: Nest can't resolve dependencies of the class AuthMiddleware {
constructor(userService) {
this.userService = userService;
console.log('userService findAll: ', userService.findAll());
}
use(req, res, next) {
console.log('AuthMiddleware called!');
next();
}
} (?). Please make sure that the argument Symbol(USER_SERVICE) at index [0] is available in the AppModule context.
I've ended up getting it to work, mostly by re-exporting the injected providers. After trying different combinations, the working one is as follows
Static dependencies (e.g. external libraries) need to be imported and re-exported inside the module decorator
Dynamic dependencies need to be imported and re-exported inside createConfigurableDynamicRootModule
Exporting the Middleware class seems to have no effect in any way
#Module({
providers: [],
imports: [ExtModule],
exports: [ExtModule],
})
export class AuthModule extends createConfigurableDynamicRootModule<
AuthModule,
AuthModuleOptions
>(AUTH_OPTIONS, {
providers: [
{
provide: AUTH_SECRET,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.secret,
},
{
provide: USER_SERVICE,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.userService,
},
],
exports: [
{
provide: AUTH_SECRET,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.secret,
},
{
provide: USER_SERVICE,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.userService,
},
],
}) {}
I am writing end-to-end tests in NestJs and using the "overrideProvider" function to setup test versions of various services (e.g. services that require database connections). I noticed though that even when I do this, the original implementation that injects the real database is still instantiated.
Is there a way to tell Nest to not create transitive dependencies that are overridden?
For example, I have a test that starts like:
...
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [ServiceModule],
})
// Works if I uncomment these lines:
// .overrideProvider('Database')
// .useValue(new TestDatabase())
.overrideProvider('ServiceUsingDatabase')
.useValue(new TestService())
.compile();
...
Where the module setup is like:
import { Inject, Injectable, Module } from '#nestjs/common';
interface Database {}
#Injectable()
class ProductionDatabase implements Database {
constructor() {
throw('Cannot create a production database.');
}
}
#Injectable()
export class TestDatabase implements Database {
constructor() {
console.log('Creating the test database.');
}
}
#Module({
providers: [
{
provide: 'Database',
useClass: ProductionDatabase
}
],
exports: ['Database']
})
class DatabaseModule {}
interface Service {}
#Injectable()
class ProductionService implements Service {
constructor(#Inject('Database') private readonly database: Database) {}
}
#Injectable()
export class TestService implements Service {
// Test implementation of the service does not Inject anything.
}
#Module({
imports: [DatabaseModule],
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: ProductionService
}
],
})
export class ServiceModule {}
But, the DI system is still seeming to try and instantiate ProductionDatabase. If I explicitly override the provider for the 'Database' it works, but I'd like to avoid having to explicitly list all transitive dependencies as such.
I ended up deciding to make a "Test" Module for every Module e.g.:
import { Inject, Injectable, Module } from '#nestjs/common';
interface Database {}
#Injectable()
class ProductionDatabase implements Database {
}
#Injectable()
export class TestDatabase implements Database {
}
#Module({
providers: [
{
provide: 'Database',
useClass: ProductionDatabase
}
],
exports: ['Database']
})
class DatabaseModule {}
#Module({
providers: [
{
provide: 'Database',
useClass: TestDatabase
}
],
exports: ['Database']
})
class TestDatabaseModule {}
interface Service {}
#Injectable()
class ProductionService implements Service {
constructor(#Inject('Database') private readonly database: Database) {}
}
#Injectable()
export class TestService implements Service {
}
#Module({
imports: [DatabaseModule],
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: ProductionService
}
],
})
export class ServiceModule {}
#Module({
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: TestService
}
],
})
export class TestServiceModule {}
etc... Though it turned out after some refactorings that the "Test" module wasn't needed as some Modules became pure business logic.
I am trying to do something fancy: a DynamicModule that chooses a Controller/Providers depending on a provider. The provider reads a config in a DB at onModuleInit, so there is no way to hard-code its values.
It is similar to this issue: https://github.com/nestjs/nest/issues/601 which was never answered...
I have tried a few approaches and the closest I have got is this:
export class FancyModule implements DynamicModule {
public module: Type<FancyModule>;
public controllers: Type<unknown>[];
public providers: Provider<unknown>[];
constructor (
#Inject(StartupConfigService) private readonly startup: StartupConfigService,
) {
this.module = FancyModule;
if (startup.featureFlagEnabled) {
this.controllers = [ FancyFeatureController ];
this.providers = [ FancyFeatureService ];
} else {
this.controllers = [ ErrorController ];
this.providers = [];
}
}
}
Second approach:
app.module.ts
#Module({
imports: [
StartupConfigModule.register(startupConfigOption),
FancyModule.registerAsync({
imports: [StartupConfigModule],
useFactory: function (startupConfigService: StartupConfigService) {
return startupConfigService.config
},
inject: [StartupConfigService],
}),
],
})
fancy.module.ts
export class FancyModule {
static registerAsync (config: IAppConfig): DynamicModule {
if (config.featureFlagEnabled) {
return {
module: FancyModule,
controllers: [ FancyFeatureController ],
providers: [ FancyFeatureService ],
};
} else {
return {
module: FancyModule,
controllers: [ ],
providers: [ ],
};
}
}
}
This injects as expected but the controllers and providers are never spun up. Is the meta-data evaluated before the constructor? Is that a bug?
It turns out what I'm trying to do is, in fact, impossible. The evaluation of the FancyModule.registerAsync() method happens before the onModuleInit od the StartupconfigService (which sets the feature flag).
A DynamicModule cannot use the contents of an instantiated provider to register() itself.
Other reading:
Fundamentals - Dynamic Modules
Advanced - Dynamic Modules
After configured cache globally like the docs, the CacheInterceptor throws an error if i use it outside the app.module.
app.module.ts
const cacheConfig = {
store: redisStore,
host: 'localhost',
port: 6379
}
#Module({
imports: [
CacheModule.register(cacheConfig),
CustomerModule,
],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: CacheInterceptor
}
]
})
export class AppModule {}
customer.module.ts
#Module({
imports: [TypeOrmModule.forFeature([CustomerRepository]), TypeOrmModule.forFeature([User])],
controllers: [CustomerController]
})
export class CustomerModule {}
customer.controller.ts
#Controller('customer')
export class CustomerController {
constructor(
#InjectRepository(CustomerRepository) private customerRepository: CustomerRepository,
#InjectRepository(User) private userRepository: Repository<User>
) {}
#Get()
#UseInterceptors(CacheInterceptor)
async get(): Promise<any> {
const user = await this.userRepository.findOne({ where: { id: 1 }, relations: ['customer'] })
console.log(user.customer.name)
const customer = await this.customerRepository.findOne({ where: { id: 1 }, select: ['id', 'name'] })
return { customer: customer.name, email: user.email }
}
}
I would like using the CacheInterceptor along any modules without import the CacheModule each one.
Nest can't resolve dependencies of the APP_INTERCEPTOR (UUID: 6aa42c77-1bac-4098-b217-1b01eb268240) (?, Reflector). Please make sure that the argument at index [0] is available in the CustomerModule context.
If you have { provide: APP_INTERCEPTOR, useClass: CacheInterceptor } you don't need to add in the #UseInterceptors() decorator in your controller. You should have the CahceInterceptor working by default with the rest of the set up