nestjs REQUEST scoped service provider - nestjs

i'm trying to use my custom logger service inside other services, but i have noticed that when i'm provide logger service to other service didn't call any lifecycle hooks anymore
there is code of my logger service
#Injectable({ scope: Scope.REQUEST })
export class LoggerService {
constructor(
#Inject(REQUEST) private readonly payload: RequestContextHost<RequestPayloadWithCorrelationId, TcpContext>
) {}
.... // other methods
}
I need this class for get unique request id and log it, but i use this service in another which implements OnModuleInit method, so when i provide logger to this service, it just don't call onModuleInit method
Maybe getting request id for logger is wrong?

Lifecycle hooks are not called for request scoped dependencies as per the docs
The lifecycle hooks listed above are not triggered for request-scoped classes. Request-scoped classes are not tied to the application lifecycle and their lifespan is unpredictable. They are exclusively created for each request and automatically garbage-collected after the response is sent.
It seems like you'll need another method around using a request scoped logger, at least for this class.

Related

NestJS: any way to create a provider for either a web-aware service, or a non web-aware service depending on context

Background
I'm working on a large application that needs to be upgraded. If I were starting from scratch I'd do this all differently. But right now I need to figure out a fix without touching hundreds of files.
For the same reason, I ideally need this code to work on Nest 6. This project needs to be upgraded to the latest nest, but there are some things that need to be fixed to do this. Before I can do that, I need to resolve the current issue, which is blocking us from upgrading off of node 12
Problem
I have a logger class. This class is supposed to pull in some information from the REQUEST context, if one is available (basically, some headers). If no request context is available, this can be ignored.
For simplicity in talking about this, we can say that I need a provider Logger which returns either a RequestAwareLogger or PlainLogger instance, depending on whether or not it is being resolved from a request scope. Alternately, I need the provider to return the same class, with either a request injected (via #Inject(REQUEST)), or left undefined.
Edit For posterity: If I were writing this from scratch, I'd just update the logger.log call to consume this information directly by passing in the request object, or the fields I needed tracked. But since this is a huge project already, I'd have to modify 1000 lines of code in different files, many of which don't have direct access to the request. This will be a longer term effort
Unfortunately, there is no built-in way to do this in Nest. However, it is possible to create a custom provider that would achieve the same effect.
Here is an example provider that would return either a RequestAwareLogger or PlainLogger instance, depending on whether or not it is being resolved from a request scope:
#Injectable()
export class LoggerProvider {
constructor(
#Optional() #Inject(REQUEST) private readonly request?: Request,
) {}
getLogger(): PlainLogger | RequestAwareLogger {
// If a request is available, return a RequestAwareLogger instance
if (this.request) {
return new RequestAwareLogger(this.request);
}
// Otherwise, return a PlainLogger instance
return new PlainLogger();
}
}
Then, you can use this provider in your logger service like so:
#Injectable()
export class LoggerService {
constructor(private readonly loggerProvider: LoggerProvider) {}
log(message: string) {
const logger = this.loggerProvider.getLogger();
// Use the logger instance
logger.log(message);
}
}
Note that this provider will only work if Nest's IoC container is used to resolve the logger service. If you are using a different IoC container (e.g. in a non-Nest application), you will need to create a custom provider for that container.

NestJS with TypeORM: When using custom repository, is a service needed anymore?

Newbie question:
When working with NestJS and TypeORM, and one has created a custom repository (which extends the standard repository), is a seperate service class needed anymore?
At the moment, I'm working only with the custom Repository class and it works fine, but I'm not sure if this is correct and perhaps has some side effects.
Btw, in another project i have no custom repo, only a service which get's two standard repo's injected, and this works also fine.
Regards,
sagerobert
I think it's up to you how much you want to add layers between typeORM and your most "front-office" code (that would be the controllers in a typical nest application).
I explain myself:
If you want, you could typically inject directly the built-in typeORM repositories into your controllers:
import {Controller, Get} from '#nestjs/common';
import {InjectRepository} from '#nestjs/typeorm';
import {Repository} from 'typeorm';
import {User} from './entities/User.entity';
#Controller()
export class AppController {
constructor(
#InjectRepository(User)
private readonly userRepository: Repository<User>,
) {
}
#Get()
async root(): Promise<User> {
return await this.userRepository.find(1);
}
}
So this would be the less layered implementation of how to retrieve the user with ID = 1.
Now, the documentation of NEST recommends to abstract this repository and inject it into a service rather than in a controller directly. This allows you to have less binding between your controller and TypeORM. Instead, it's your service that has this binding. If you have many controllers that use this repository, and you decide that you want to change TypeORM and use the new fancy ORM, you'll have to change every controller.
Now, if you just inject the repository inside your service and use this service into all your controllers, you will just have to change the implementation of your service and all the controllers will remain the same.
Secondly, imagine that you want to test your application. You will face the same problem. How can you run your tests without an SQL connection? I suppose that your unit tests are not created to test the TypeORM behaviour, but instead written to test YOUR code behavior.
It will be much easier to mock a repository injected in a service than mock all repositories injected in your controllers.
So to conclude this answer, I think that this question should be closed because it is primarily opinion-based. But IMO, the dreamed architecture is the following:
Create a Custom Repository that extends the TypeORM Repository.
Inside the Custom Repository, add methods that use the Query Builder.
Inject this Custom Repository into your services
Inject the services into your controllers.
Don't ever use the query builder into controllers because it is hard to mock.
I hope this answers to your question: A service class is not needed. But it will help you keep your code clean.

Is there a way to remove the "/json/reply/" section of the url?

I would like the URL for a request to be /AmazingRequest (or even /AmazingService) instead of /json/reply/AmazingRequest.
I've tried the Route attribute, but it seems to have no effect. Is it possible within ServiceStack, or would I have to resort to URL rewriting?
This is what I've tried. It compiles, but the attribute has no effect.
public class MyServiceEndpoints : IService
{
[Route("/AmazingService")]
public AmazingResponse Post(AmazingRequest request)
{
return new Amazing(request).GetResponse();
}
}
I realize I would need to tell ServiceStack that it is a json request, but I'm fine with adding the Accept and Content-Type headers or maybe even a ?format=json to the query string.
P.S. I'm using the BSD version of ServiceStack
In ServiceStack Routes are defined on the Request DTO as it's part of your Service Contract, e.g:
[Route("/AmazingService")]
public class AmazingRequest { ... }
The pre-defined Route you're using is because ServiceStack doesn't think there's any custom route defined for your Service and just uses the default one.
The alternative way for declaring your Routes is to use the Fluent Registration API in your AppHost, e.g:
public void Configure(Container container)
{
Routes
.Add<AmazingRequest>("/AmazingService");
}
But the benefit of defining them on the Request DTO's is that your .NET Service Clients will also have access to them and will be able to use your custom routes instead of falling back to the pre-defined routes.

Dependency injection of IAuthSession resolves empty session

I am successfully using ServiceStack's credentials authentication along with a custom AuthUserSession. What I can't work out is how to use this in classes that have had dependencies injected via Funq.
I have several "Business" Classes which rely on Funq to inject the DAL dependency into them, this works perfectly.
However, within the DAL I obviously want to mark database fields such as "CreatedBy" and "ModifiedBy" with the currently logged in user id. So, I create a constructor parameter in the DAL to receive the AuthUserSession.
Within Global.asax, the following lines are in place:
//authentication
Plugins.Add(new AuthFeature(() => new MyAuthUserSession(),
new IAuthProvider[] { new MyCredentialsAuthProvider() }));
No matter how I register MyAuthUserSession with funq, it resolves to an instance with no Session information in it.
container.Register<IAuthSession>(new MyAuthUserSession());
or
container.Register(new MyAuthUserSession());
In a nutshell; What is the best way of getting "Current User ID" into a class via constructor injection?
UserSessions are not dependencies that are resolved from an IOC and as such should never be registered in an IOC which would be meaningless and cause confusion on how they actually work.
They are models that are hydrated from a Cache at runtime using the client's Cookies that are passed in with each HTTP Request. The Session wiki has more information about how Sessions work in ServiceStack.
Custom Typed User Sessions can be resolved in ServiceStack using the base.SessionAs<T> method, e.g:
public class MyService : Service
{
public MyDependencyThatUsesUserSession MyDep { get; set; }
public object Any(Request request)
{
MyAuthUserSession mySession = base.SessionAs<MyAuthUserSession>();
return MyDep.Execute(mySession, request.Id);
}
}

ServiceStack: Adding routes dynamically

I have not tried this yet, but I would like each module (Silverlight) to register its own routes, rather then adding it in application start.
Can routes be added to AppHost after application start, or do they all have to be immediatelly registered during Configure step?
I am thinking to scan all assemblies at the startup and provide AppHost with all assemblies that implement service stack services, but let each module add its own routes (have not figured out yet exact mechanism.
Before I go down this route, need to know if it is possible to add routes after the Configure step.
All configuration and registration in ServiceStack should be done within the AppHost.Configure() method and remain immutable thereafter.
If you want to encapsulate registrations of routes in a module than package it as a Plugin and register them manually on IPlugin.Register(IAppHost).
Here are some different ways to register routes:
public class MyModule : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.Routes.Add<MyRequestDto>("/myservice", "POST PUT");
appHost.Routes.Add(typeof(MyRequestDto2), "/myservice2", "GET");
appHost.RegisterService(typeof(MyService), "/myservice3");
}
}
Then inside your AppHost.Configure you would register the Plugin, e.g:
Plugins.Add(new MyModule());

Resources