How to inject service with parameter and global service in constructor? - nestjs

I have service which use service form global module and parametr.
#Injectable()
export class UsersService {
_paramentr;
constructor(
parametr,
GlobalService,
) {
this._parametr = parametr;
}
}
How to inject UsersService to AnotherService with paramentr.
I know, I can do it by new UsersService, but inside UserService I can use anothore services, I must inject it from top level to deep into. It's not very convenient.

Related

NestJS onModuleInit not working when specific service is added to constructor

I have a bunch of services defined in my NestJS project. Some of the services are used as common services in a lot of other services. So they are imported in a few modules as well. But I noticed that when specific service srvD is imported in another service srvE2, the onModuleInit is not being called when the project starts.
The project starts without any error. Not really sure what's happening.
An example of the project structure. Issue is in srvE2
srvA.ts
#Injectable()
export class SrvA {
constructor(
private somePkgSrv: SomePkgSrv,
) {}
}
srvB.ts
#Injectable()
export class SrvB {
constructor(
private srvA: SrvA,
) {}
}
srvC.ts
#Injectable()
export class SrvC {
constructor(
private srvA: SrvA,
private srvB: SrvB,
) {}
}
srvD.ts
#Injectable()
export class SrvD {
constructor(
private srvA: SrvA,
private srvB: SrvB,
private srvC: SrvC,
) {}
}
srvD.module.ts
#Module({
providers: [SrvA, SrvB, SrvC, SrvD],
exports: [SrvD],
})
srvE1.ts
export class SrvE1 implements OnModuleInit {
constructor(
private srvA: SrvA,
private srvB: SrvB,
private srvC: SrvC,
) {}
async onModuleInit() {
console.log ('I can print! Yay!')
}
}
srvE2.ts
export class SrvE2 implements OnModuleInit {
constructor(
private srvA: SrvA,
private srvB: SrvB,
private srvC: SrvC,
private srvD: SrvD,
) {}
async onModuleInit() {
console.log ('I refuse to print so long as SrvD is here. Comment it and I will
print')
}
}
srvE.module.ts
#Module({
import: [SrvD], // the module
providers: [SrvE1, SrvE2], // the services
exports: [SrvE1, SrvE2],
})
I am dealing with a similar issue. Based on the comments here I tried changing the scoping for my new dependency from Scope.REQUEST to Scope.DEFAULT and that resolved my issue.
Request scoped providers spread this scope to whole chain of injection. Consequently, if you use it, even deeply inside your code, then onModuleInit won't run.
That being said, well ... request scope isn't very recommended, performance wise. Here is an interesting article : Why You Should Avoid Using Request-scoped Injection in NestJS
I've also bumped into this question & its answers : How to inject a request scoped provider at NestJS controller?
I've tried nj-request-scope package from #profes, and it works fine, especially to fix your problem.
If you don't want to use it, you can still try to move your onModuleInit code in a default scoped service.

Nest.js custom Inject decorator with params

I am trying to write custom Inject decorator, so I can have access to instance of service that is being injected and pass my data there somehow. But can't reach any success. I am not sure if this is right direction and may be there are better ways to implement this idea
e.g I have Service
#Injectable()
class S3Service {
public getBucket(){
console.log(this.bucket);
}
}
class MyOtherService{
constructor(
#InjectWithParams({bucket: 'fooBucket'})
private readonly customS3Service: S3Service
){
this. customS3Service.getBucket() // may log fooBucket
}
}
class MyOtherOtherService{
constructor(
#InjectWithParams({bucket: 'barBucket'})
private readonly customS3Service: S3Service
){
this. customS3Service.getBucket() // may log barBucket
}
}
I tried few options, like (In Nest.js, how to get a service instance inside a decorator?), but I still can't access service instance in decorator
I suggest you to do this
class MyOtherOtherService{
constructor(
#InjectWithParams({bucket: 'barBucket'})
private readonly customS3Service: S3Service
){
customS3Service.getBucket() // may log barBucket
}
}
because you call it on your constructor you must remove 'this.'

How to manually inject dependency in nestjs

In angular we can manually access and inject dependencies using the built in Injector class. By which you can access Injectables and Inject them without actually passing them in the constructor. Basically I want to inject a service to another service without passing it as an arg to the constructor.
This is the angular equivalent Inject a service manually
I wanted to achieve similar thing in nestjs
Note : The service to be injected also has a dependency, so I can't just instantiate it
I believe what you're looking for is Nest's ModuleRef class, where you can do something like the following:
#Injectable()
export class CatsService implements OnModuleInit {
private service: Service;
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
this.service = this.moduleRef.get(Service);
}
}
Where Service should actually be the class you are wanting to inject.
I think #Inject('token') should work as you expected
import { Injectable, Inject } from '#nestjs/common';
#Injectable()
export class HttpService<T> {
#Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
ref: https://docs.nestjs.com/providers#property-based-injection

Inject services on demand on Guard

I have a guard that checks the ownership of a resource. If the user is the owner (created that resource), then he can access (update, read, delete).
Each resource is handled by its own service (comments is handled by the CommentsModule which has the CommentsService and so on). Each service that handles a resource with ownership implements a function called hasOwnership and the guard will call said function.
If possible, I would like my guard to inject the correct service depending on the controller that is calling it. So, if CommentsController is calling the guard, then it should inject and use CommentsService.hasOwnership.
I have tried using dynamic modules to inject the correct module/service on the imports of ACModule which hosts the guard, but that seems to be a no go as I can't properly handle the circular dependencies.
Injecting every service into the guard and selecting the correct will be very troublesome to mantain, due to the circular dependencies.
Is there a better way? This would be the desirable behavior.
#Injectable()
export default class ACGuard implements CanActivate {
constructor(
#Inject('SERVICE_KEY') private correctService
) {}
canActivate(context: ExecutionContext) {
return await correctService.hasOwnership();
}
}
Because you need a different service at run time (instead of at compile time) you are going to need to take a factory approach. It is a little troublesome to maintain as new resources are added but that's the trade-off you have to make.
First thing I would do is create a factory class to determine which service is the correct service to use based on the ExecutionContext:
export interface IService {
hasOwnership() : Promise<boolean>;
}
#Injectable()
export class ServiceFactory {
//Make sure every service returned from this method implements the "IService" interface
public getCorrectService(context: ExecutionContext) : IService {
if(context...) {
return new CommentsService();
} else if(context...) {
return new SomeOtherService();
}
}
}
Now you can inject that factory into your guard to get the correct service:
#Injectable()
export default class ACGuard implements CanActivate {
constructor(private serviceFactory: ServiceFactory) {}
canActivate(context: ExecutionContext) {
//Here's where the magic of this happens...
const correctService: Iservice = this.serviceFactory.getCorrectService(context);
return await correctService.hasOwnership();
}
}

Nest.js get injector instance

I want to create an instance of a dynamically loaded class trough Nest.js dependency injection service.
In Angular I would use Injector.create, what would be the equivalent in Nest.js ?
First of all you should get a ModuleRef which references current module, and then use its "get" method to get an instance.
#Injectable()
export class AppletService {
files: FileService;
constructor(
private moduleRef: ModuleRef,
) {
this.files = moduleRef.get(FileService);
}
}

Resources