I'm trying to create my custom JwtModule. I made JwtModule a dynamic module so that I can provide a private key using dotenv.
jwt.module.ts :
import { DynamicModule, Module } from '#nestjs/common';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtModuleOptions } from './jwt.interfaces';
import { JwtService } from './jwt.service';
#Module({})
export class JwtModule {
static forRoot(options: JwtModuleOptions): DynamicModule {
return {
module: JwtModule,
providers: [
{
provide: CONFIG_OPTIONS,
useValue: options,
},
JwtService,
],
exports: [JwtService],
};
}
}
jwt.service.ts :
import { Inject, Injectable } from '#nestjs/common';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtModuleOptions } from './jwt.interfaces';
#Injectable()
export class JwtService {
constructor(
#Inject(CONFIG_OPTIONS) private readonly options: JwtModuleOptions,
) {}
// I will do something here
}
users.module.ts :
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { JwtModule } from 'src/jwt/jwt.module';
import { User } from './entities/user.entity';
import { UsersResolver } from './users.resolver';
import { UsersService } from './users.service';
#Module({
imports: [TypeOrmModule.forFeature([User]), JwtModule],
providers: [UsersResolver, UsersService],
})
export class UsersModule {}
users.service.ts :
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { InternalServerErrorOutput } from 'src/common/common.constants';
import { JwtService } from 'src/jwt/jwt.service';
import { Repository } from 'typeorm';
import {
CreateAccountInput,
CreateAccountOutput,
} from './dtos/create-account.dto';
import { User } from './entities/user.entity';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly jwtService: JwtService,
) {}
// I will do something here
}
I imported JwtModule into app.module.ts like this :
JwtModule.forRoot({ privateKey: process.env.JWT_PRIVATE_KEY })
However, when I try to run this application, the console prints an error:
Nest can't resolve dependencies of the UsersService (UserRepository, ?).
Please make sure that the argument JwtService at index [1] is available in the UsersModule context.
Potential solutions:
- If JwtService is a provider, is it part of the current UsersModule?
- If JwtService is exported from a separate #Module, is that module imported within UsersModule?
#Module({
imports: [ /* the Module containing JwtService */ ]
})
Since I exported JwtService and imported JwtModule into UsersModule, so what the error suggests does not help. Using #Global to JwtModule is a simple solution, but as I will not use JwtModule elsewhere, so I would like to know the way without making it global. Any help would be greatly appreciated. Thank you!
Dynamic modules are just that, dynamic. They need to have their configuration/registration methods called when they are about to be made of use. You register the JwtModule in your AppModule, but not in your UserModule, so UserModule doesn't see the returned module with the JwtService exported, it sees a module with no providers and no exports. There's a couple of ways around this,
you can register the JwtModule in the UserModule directly, if you aren't going to re-use this, it might not be a bad idea
you can create a wrapper module tat imports the JwtModule.forRoot() and exports the JwtModule, then you import the wrapper module where you need the JwtModule and get back the configured module each time
this is mostly overkill, but cool to know, you can follow the RxJS Subject approach in this repository that would allow you to do something like JwtMoule.externallyConfigured(0) and get the configuration that was made in AppModule. Again, overkill, but cool
Related
I have a class that is not related to any nest modules. I wanted to import and use my ConfigService inside of this class. Can I do this without adding this class to any module or do I need to have a module in order to use nest generated classes?
Thanks
You can create on class like below
import { Injectable } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
#Injectable()
export class AppConfig {
static service: ConfigService;
constructor(service: ConfigService) {
AppConfig.service = service;
}
static get(key: string): any {
return AppConfig.service.get(key);
}
}
add this class as a provider in your root module AppModule,
...
providers: [AppConfig],
...
now once the app is booted, you can use it as following anywhere inside app,
AppConfig.get('database.port');
Hope this helped!
Yep, you can get anything from Nest.js DI container using app.get method.
e.g.
const configService = app.get(ConfigService)
Building on Vinayak Sarawagi's solution, you can even do this inside your custom ConfigModule:
import { Injectable } from '#nestjs/common'
import { ConfigService } from '#nestjs/config'
#Injectable()
export class MyConfigService {
static config: MyConfigService
constructor(private configService: ConfigService) {
MyConfigService.config = this
}
get isDevelopment(): boolean {
return this.configService.get('NODE_ENV') === 'development'
}
}
you can then use whatever private property you have on that service
AppConfig.config.isDevelopment
See the linked answer for details how to export the module globally.
I get such error:
Error: Nest can't resolve dependencies of the CreateGroupTask (TaskQueueService, GroupsService, ?, GroupNotificationsService, GroupRepository, Logger). Please make sure that the argument dependency at index [2] is available in the TasksModule context.
CreateGroupTasks service looks this way:
import { Logger } from 'nestjs-pino';
import { GroupNotificationsService } from '../bll/group/group-notifications.service';
import { Injectable, OnApplicationBootstrap } from '#nestjs/common';
import { GroupsService } from '../bll/group/groups.service';
import { PotentialGroupsService } from '../bll/potential-groups/potential-groups.service';
import { GroupRepository } from '../dal/user-group/group.repository';
import { TaskQueueService } from '../task-queue/task-queue.service';
export const CREATE_GROUP_TASK_NAME = 'create-group';
const CREATE_GROUP_TASK_DOMAIN = 'create-group-task';
#Injectable()
export class CreateGroupTask implements OnApplicationBootstrap {
constructor(
private readonly taskQueueService: TaskQueueService,
private readonly groupsService: GroupsService,
private readonly potentialGroupService: PotentialGroupsService,
private readonly groupNotificationsService: GroupNotificationsService,
private readonly groupsRepository: GroupRepository,
private readonly logger: Logger,
) {}
onApplicationBootstrap() {
this.taskQueueService.registerTaskHandler(
CREATE_GROUP_TASK_NAME,
this.handle.bind(this),
);
}
async handle([potentialGroupId]: [number]) { //handler code }
}
PotentialGroupsService exported from BusinessLogicModule module as well as GroupsService, and GroupNotificationsService:
import { Module } from '#nestjs/common';
import { SendAuthLinkTask } from './send-auth-link.task';
import { CallReminderTask } from './call-reminder-task';
import { TaskQueueModule } from '../task-queue/task-queue.module';
import { RepositoryModule } from '../dal/repository.module';
import { CommunicationsgModule } from '../communication/communications.module';
import { BusinessLogicModule } from '../bll/business-logic.module';
import { SendStatsTask } from './send-stats.task';
import { WorkoutNotificationTask } from './workout-notification.task';
import { SayWelcomeTask } from './say-welcome.task';
import { CreateGroupTask } from './create-group.task';
import { GroupStartsReminderTask } from './group-starts-reminder.task';
#Module({
imports: [
TaskQueueModule,
CommunicationsgModule,
RepositoryModule,
BusinessLogicModule,
],
providers: [
SendAuthLinkTask,
CallReminderTask,
SendStatsTask,
WorkoutNotificationTask,
SayWelcomeTask,
CreateGroupTask,
GroupStartsReminderTask,
],
})
export class TasksModule {}
And all those services exported from the BusinessLogicModule:
import { Module } from '#nestjs/common';
import { CommunicationsgModule } from '../communication/communications.module';
import { RepositoryModule } from '../dal/repository.module';
import { AuthService } from './auth-logic/auth.service';
import { SessionService } from './auth-logic/session.service';
import { GroupNotificationsService } from './group/group-notifications.service';
import { UserService } from './user/user.service';
import { MessageService } from './conversation/message.service';
import { ConversationService } from './conversation/conversation.service';
import { GroupsService } from './group/groups.service';
import { StatsService } from './statistics/workouts-stats';
import { InvitationsService } from './invitations/invitations.service';
import { TaskValidator } from './group/notification-task-validator.service';
import { PotentialGroupsService } from './potential-groups/potential-groups.service';
import { PotentialGroupCheckerCron } from './potential-groups/potential-groups-checker.cron';
#Module({
imports: [CommunicationsgModule, RepositoryModule],
providers: [
AuthService,
SessionService,
GroupNotificationsService,
UserService,
MessageService,
ConversationService,
GroupsService,
StatsService,
InvitationsService,
TaskValidator,
PotentialGroupsService,
PotentialGroupCheckerCron,
],
exports: [
AuthService,
SessionService,
GroupNotificationsService,
UserService,
MessageService,
ConversationService,
GroupsService,
StatsService,
InvitationsService,
TaskValidator,
PotentialGroupsService,
],
})
export class BusinessLogicModule {}
What do I do wrong? Why nest can't resolve PotentialGroupService?
From the error:
Error: Nest can't resolve dependencies of the CreateGroupTask (TaskQueueService, GroupsService, ?, GroupNotificationsService, GroupRepository, Logger). Please make sure that the argument dependency at index [2] is available in the TasksModule context.
Nest can't resolve the argument dependency. Usually when this happens there is a circular file import that can't be resolved, meaning the dependency comes in as undefined, so Nest provides a generic name for the dep. Check your file imports (including barrel files) and make sure you're not creating a circular import on accident.
It can't resolve PotentialGroups.
Make sure that PotentialGroupsModule is imported inside BusinessLogicModule.
And also make sure that PotentialGroupsService is exported from PotentialGroupsModule
I have an app that receives a service as a dependency on the controller, so far so good, but I would like to find a way to instead of declaring the specific implementation of that service, to be able to "ask" from the controller for the interface that this service implements to decouple of the concrete implementation of that service. How is this done in nest js?
To do this you have to create an injection token for your interface and use the #Inject() decorator with the injection token when injecting your service. Then in your module you can declare which implementation to provide for that injection token.
Below is a simple greeting service interface and our injection token that will be used when registering our service as a provider.
greeting-service.interface.ts
// This will be our injection token.
export const GREETING_SERVICE = 'GREETING SERVICE';
export interface IGreetingService {
greet(name: string): Promise<string>;
}
A basic service that will implements our greeting interface...
professional-greeting.service.ts
import { Injectable } from '#nestjs/common';
import { IGreetingService } from './greeting-service.interface';
#Injectable()
export class ProfessionalGreetingService implements IGreetingService {
public async greet(name: string): Promise<string> {
return `Hello ${name}, how are you today?`;
}
}
And our greeting module where we register our service using the token...
greeting.module.ts
import { Module } from '#nestjs/common';
import { ProfessionalGreetingService } from './services/professional-greeting.service';
import { GREETING_SERVICE } from './services/greeting-service.interface';
import { GreetingController } from './controllers/greeting.controller';
#Module({
providers: [
{
// You can switch useClass to different implementation
useClass: ProfessionalGreetingService,
provide: GREETING_SERVICE
}
],
controllers: [
GreetingController
]
})
export class GreetingModule {}
Now when we inject our service, we can use the #Inject() decorator with our injection token. Whichever implementation you provived to useClass in our GreetingModule will be injected...
greeting.controller.ts
import { Controller, Get, Inject, Query } from '#nestjs/common';
import { GREETING_SERVICE, IGreetingService } from '../services/greeting-service.interface';
#Controller('greeting')
export class GreetingController {
constructor(
#Inject(GREETING_SERVICE)
private readonly _greetingService: IGreetingService
) {}
#Get()
public async getGreeting(#Query('name') name: string): Promise<string> {
return await this._greetingService.greet(name || 'John');
}
}
https://jasonwhite.xyz/posts/2020/10/20/nestjs-dependency-injection-decoupling-services-with-interfaces/
https://github.com/jmw5598/nestjs-di-decoupling-with-interfaces
https://docs.nestjs.com/fundamentals/custom-providers
When I use Custom Repository class without any extension then I run into the error: No repository for "MasterDataRepo" was found. Looks like this entity is not registered in current "default" connection?
#EntityRepository()
export class MasterDataRepo {
constructor(private manager: EntityManager) {
}
getDriveryByTerminal(terminalCode: string):Promise<Driver> {
return this.manager.findOne(Driver, { terminalCode });
}
}
The app works fine if I create repository class after extending Repository<Entity>
#EntityRepository(Driver)
export class MasterDataRepo extends Repository<Driver> {
getDriveryByTerminal(terminalCode: string):Promise<Driver> {
return this.manager.findOne(Driver, { terminalCode });
}
}
My MasterDataModule is:
import { Module } from '#nestjs/common';
import { MasterDataController } from './MasterData.controller';
import { MasterDataService } from './masterData.service';
import { TypeOrmModule } from '#nestjs/typeorm';
import { MasterDataRepo } from './MasterData.Repo';
#Module({
imports: [
TypeOrmModule.forFeature([MasterDataRepo])
],
controllers: [MasterDataController],
providers: [MasterDataService],
})
export class MasterDataModule {}
and AppModule is:
import { Module } from '#nestjs/common';
import { TasksModule } from './tasks/tasks.module';
import { TypeOrmModule } from '#nestjs/typeorm';
import { typeOrmConfig } from './config/typeorm.config';
import { MasterDataModule } from './masterData/MasterData.module';
import { Connection } from 'typeorm';
#Module({
imports: [
TypeOrmModule.forRoot(typeOrmConfig),
TasksModule,
MasterDataModule
],
})
export class AppModule {
constructor(private connection: Connection) {}
}
TypeOrmModule.forFeature([MasterDataRepo])
TypeOrmModule.forFeature accepts array of Entities and your MasterDataRepo is not an entity.
But when you use EntityRepository decorator with the User entity, it will add your User entity to the available entities list(which will be applied to the database connection).
Also extending the Repository class will make your MasterDataRepo as an extended version of an Entity.
https://github.com/typeorm/typeorm/blob/906d97fc8dbf1dba8f4e579a4f5bfead83af36ab/src/decorator/EntityRepository.ts
https://github.com/typeorm/typeorm/blob/906d97fc8dbf1dba8f4e579a4f5bfead83af36ab/src/repository/Repository.ts
Note:
There are two solutions:
Nestjs already provides #nestjs/typeorm module and you can inject a repository for an entity easily in your service.
For example:
#Injectable()
export class UsersService {
constructor(
#InjectRepository(User)
private usersRepository: Repository<User>
) {}
...
}
You can check the documentation here - https://docs.nestjs.com/techniques/database
You can just use Active Record pattern that doesn't require a repository.
What you need to do is just to make your entities extend BaseEntity of typeorm.
For example:
#Entity()
export class User extends BaseEntity {} // <- BaseEntity
...
const user = await User.find({}) // You just use the User class for executing a query.
user.name = 'something';
await user.save();
Thanks, #yash. I could find a workaround. Defined the repo as injecatable
#Injectable()
#EntityRepository()
export class MasterDataRepo {
constructor(private manager: EntityManager) {
}
getDriveryByTerminal(terminalCode: string):Promise<Driver> {
return this.manager.findOne(Driver, { terminalCode });
}
}
and the service as
#Injectable()
export class MasterDataService {
constructor(
private masterDataRepo: MasterDataRepo,
) {}
}
Do you see any concerns?
For those using TypeORM and who had the Injection configured correctly but still running into the error:
In my case, the issue was that my #Entity was not registered in the Postgres TypeOrmModuleOptions (more precisely the entity array of the BaseConnectionOptions, cf their git repo ). Those options allow to configure Nest's TypeOrmModule
Hope this helps someone else coming to this thread
I have custom module AuthModule with AuthService, AuthController with routes and so on, that work fine, and want to share this module between several projects as a package. The questions is how to extend imported AuthService from this package and how to inject in it additional services?
More details
Into my custom AuthService, that I want to place in package, is injected class UserService, that by default gets from the database some User data and return it to client side . I need to inject into AuthService another, for example ProfileService from application, that get from database User extra data. The aim is to merge User main data and User extra data, and return this bunch to client
I don't see really the need for a Dynamic Module here.
Indeed, only is needed that the service you want to inject into other entity managed by NestJs, is exported from your AuthModule, and AuthModule imported in the Module you want other entities have injected your AuthService.
import { Module } from '#nestjs/common'
import { AuthService } from './AuthService'
#Module({
providers: [
AuthService,
// ... others
],
exports: [
AuthService
]
})
export class CommonModule {}
Then in your dependent class (let's say another Controller, but could be anything like a GraphQL Resolver, Interceptor, or whatever), you declare the dependency on AuthService.
import { AuthService } from '../auth/AuthService'
import { Dependencies } from '#nestjs/common'
#Dependencies(AuthService)
export class DependentController {
constructor (authService) {
this.authService = authService
}
}
Finally, in order to the AuthService be available to the dependent controller, it has to be imported in the same Module that controller is provided.
import { Module } from '#nestjs/common'
import { CommonModule } from '../auth/CommonModule'
import { DependentController } from './controller/DependentController'
#Module({
imports: [
CommonModule,
// ...others
],
providers: [
DependentController,
// ...others
]
})
export class ControllerModule {}
I understand that the syntax could be shorter and without using Dependencies decorator in typescript, however the core of the matter is exporting the shared Service from the Module it is provided, then importing that module into the Module other classes that require it.
This is one of the use cases Dynamic Modules have been created for.
import { Module, DynamicModule } from '#nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';
#Module({
providers: [Connection],
})
export class DatabaseModule {
static forRoot(entities = [], options?): DynamicModule {
const providers = createDatabaseProviders(options, entities);
return {
module: DatabaseModule,
providers: providers,
exports: providers,
};
}
}
In this example, see how providers are given (dynamically) to the result module.
Your package should follow the same pattern: expose a static method which allows building a DynamicModule, so you can resolve your other services (internal or external) as you want (building your own array of providers).
I'm using GraphQL and my resolver is decorated with #Resolver(),
so I had to not use #Dependencies (since I got a TypeError, that my functions are not functions).
I exported my service:
import { Module } from '#nestjs/common';
import { UserService } from './user.service';
import { UserResolver } from './user.resolver';
#Module({
providers: [UserResolver, UserService],
exports: [UserService], //This line is new
})
export class UserModule {}
Then, I imported it in the other module:
import { Module } from '#nestjs/common';
import { IngredientService } from './ingredient.service';
import { IngredientResolver } from './ingredient.resolver';
import { UserModule } from 'src/user/user.module'; //This line is new
#Module({
providers: [IngredientResolver, IngredientService],
imports: [UserModule], //This line is new
})
export class IngredientModule {}
Then, in my resolver, I added it in the constructor:
import { UserService } from 'src/user/user.service'; //This line is new
#Resolver(() => Ingredient)
export class IngredientResolver {
constructor(
private readonly ingredientService: IngredientService,
private readonly userService: UserService, //This line is new
) {}