I have a request scoped injectable for logging.
f.e.
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class RequestLogger {
public log(message: string) {
console.log(message);
}
}
(disregard for a moment that it doesn't use a constructor with the request yet; that's beside the point)
And I also have a controller with some singleton injections.
I'd like to inject the request injectable in a way that will allow the controller to be initiated just once. Injecting it at the constructor will make the controller be recreated on each request, so surely that's not the way (is it?).
I tried to place it in the method signature, but that doesn't seem to do anything.
e.g.
@Controller('register')
export class RegisterApiController {
public constructor(
private readonly registerService: RegisterService,
) {
console.log('Controller initiated');
}
@Post()
public async postIndex(
@Inject(RequestLogger) logger: RequestLogger,
): Promise<unknown> {
console.log('request made');
logger.log('Logger message to log');
return this.registerService.register();
}
}
after application bootstrap (that also includes "Controller initiated"), each request terminates with error 500, and in the console
Request made
[TypeError] Cannot read property 'log' of undefined
Is there a way to inject a request scoped injectable without forcing a recreation of the controller that uses it? What is it?
If there isn't another way, is there at least a way to move initial controller logic somewhere else, so that what needs to be done once on first controller init can be done there?
This is possible with the library I've created recently, which it's free from bubbles up injection chain and performance issues:
https://github.com/kugacz/nj-request-scope
After register RequestScopeModule
in your module class:
import { RequestScopeModule } from 'nj-request-scope';
@Module({
imports: [RequestScopeModule],
})
your example request-scope RequestLogger
class would look like this:
import { Injectable, Scope } from '@nestjs/common';
import { RequestScope } from 'nj-request-scope';
@Injectable()
@RequestScope()
export class RequestLogger {
public log(message: string) {
console.log(message);
}
}
and in your example controller, add the class field logger
.
The logger
field will be a new RequestLogger
instance created for every request without recreating a new instance of RegisterApiController.
@Controller('register')
export class RegisterApiController {
public constructor(
private readonly registerService: RegisterService,
private readonly logger: RequestLogger,
) {
console.log('Controller initiated');
}
@Post()
public async postIndex(): Promise<unknown> {
console.log('request made');
logger.log('Logger message to log');
return this.registerService.register();
}
}
Another example you can find here: https://github.com/kugacz/nj-request-scope-example/tree/main/src/request.scope
With nestjs, unfortunately, this is not possible, see the docs:
Scope bubbles up the injection chain. A controller that depends on a request-scoped provider will, itself, be request-scoped.
You can, however, inject the initial controller logic into the controller as a singleton, so that it won't be executed again for every request.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With