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

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.

Related

what is getRepositoryToken in nestjs typeorm and when to use it?

The docs here are as below:
When it comes to unit testing an application, we usually want to avoid
making a database connection, keeping our test suites independent and
their execution process as fast as possible. But our classes might
depend on repositories that are pulled from the connection instance.
How do we handle that? The solution is to create mock repositories. In
order to achieve that, we set up custom providers. Each registered
repository is automatically represented by a Repository
token, where EntityName is the name of your entity class.
The #nestjs/typeorm package exposes the getRepositoryToken() function
which returns a prepared token based on a given entity.
What does that even mean? Autocomplete docs just give the signature with no explanation.
getRepositoryToken() is a helper method that allows you to get the same injection token that #InjectRepository() returns. This is useful when it comes to tests so that you can define a custom provider that has a matching token for the DI resolution, and so you can provide a mock of the Repository methods without the need to actually talk to the database. So for example, if you have
#Injectable()
export class FooService {
constructor(#InjectRepository(Foo) private readonly fooRepo: Repository<Foo>) {}
}
In your test you can add the provider
{
provide: getRepositoryToken(Foo),
useValue: {
find: jest.fn(),
insert: jest.fn(),
},
}
And now you've got a mock injectable provider for the Repository.
The biggest reason that things have to be done this way is because typescript doesn't reflect generic classes, it only reflects Repository, and if Nest tries to figure out which repository you mean to inject, with just that name (Repository) it's most likely going to get it wrong and inject the wrong class. Using #InjectRepsitory() allows for setting the proper injection token.

NestJS object instantiation performance

in NestJS, it is possible to define injection scope of injectable objects. (possibilities are: singleton, request, transient)
in the doc it is recommend to always use singleton scopes wherever possible, since f.e request scope will make nest instantiate providers on each request-response cycle, which can have an affect on the application's performance
Using request-scoped providers will have an impact on application performance. While Nest tries to cache as much metadata as possible, it will still have to create an instance of your class on each request. Hence, it will slow down your average response time and overall benchmarking result. Unless a provider must be request-scoped, it is strongly recommended that you use the default singleton scope.
Since when Object instantiation consumes so much resources?
that the entire framework is built around singleton instances,
making those instances kind of "state-full" in the sense where I can't really use "instance variable" safely, without the concern that 2 requests/consumers may use the same provider instance concurrently (due to the async nature of node) and conflict each other's instance variables?
here is what im trying to do:
export class MyClass {
constructor (...some dependencies) {
}
someMethod(payload){
this.myInstanceVariable = payload.data;
someMethod2();
}
someMethod2(){
// this.myInstanceVaraible usage, which i cant really trust it contains what i expect it to
}
}
I come from a ruby background.
when i wrote services in ruby, each request would instantiating whatever services it needs, and once the request was done, all those objects would be automatically garbage-collected.
that way i could safely define instance-variables on my services - without being concerned about
"instances conflicts".
NestJS supports injection scopes. There are provider scopes, controller scopes.
In your case if you need to use the instance variable with a provider, you might be able to use the REQUEST scope which would create a new object instance per request.
#Injectable({ scope: Scope.REQUEST })
export class CatsService {}
You can read more on this at https://docs.nestjs.com/fundamentals/injection-scopes

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.

Servicestack Multitenancy dynamic plugins

We are moving from an on premise-like application to a multi tenant cloud application.
for my web application we made a very simple interface based on IPlugin, to create a plugin architecture. (customers can have/install different plugins)
public interface IWebPlugin : IPlugin
{
string ContentBaseUrl { set; get; }
}
We have some plugins that would normally be loaded in on startup. Now i'm migrating the code to load at the beginning of a request (the Register function is called on request start), and scope everything inside this request.
It's not ideal but it would bring the least impact on the plugin system for now.
I could scope the Container by making an AppHost child container which would stick to the request:
Container IHasContainer.Container
{
get
{
if (HasStarted)
return ChildContainer;
return base.Container;
}
}
public Container ChildContainer
{
get { return HttpContext.Current.Items.GetOrAdd<Container>("ChildContainer", c => Container.CreateChildContainer()); }
}
problem case
Now im trying to make plugins work that actually add API services.
appHost.Routes.Add<GetTranslations>("/Localizations/translations", ApplyTo.Get);
But this service is unreachable (and not visible in metadata). How do i make it reachable?
I see you execute the following in ServiceController AfterInit. Re-executing this still wouldnt make it work.
//Copied from servicestack repo
public void AfterInit()
{
//Register any routes configured on Metadata.Routes
foreach (var restPath in appHost.RestPaths)
{
RegisterRestPath(restPath);
//Auto add Route Attributes so they're available in T.ToUrl() extension methods
restPath.RequestType
.AddAttributes(new RouteAttribute(restPath.Path, restPath.AllowedVerbs)
{
Priority = restPath.Priority,
Summary = restPath.Summary,
Notes = restPath.Notes,
});
}
//Sync the RestPaths collections
appHost.RestPaths.Clear();
appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
appHost.Metadata.AfterInit();
}
solution directions
Is there a way i could override the route finding? like extending RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
Or could i restart the path compilation/caching? (would be enough for now that the service would be reachable tenant wide )
All configuration in ServiceStack should be contained within AppHost.Configure() and remain immutable thereafter. It's not ThreadSafe to modify ServiceStack's Static Configuration at runtime like trying to modify registered routes or Service Metadata which needs to be registered once at StartUp in AppHost.Configure().
It looks as though you'll need to re-architect your solution so all Routes are registered on Startup. If it helps Plugins can implement IPreInitPlugin and IPostInitPlugin interfaces to execute custom logic before and after Plugins are registered. They can also register a appHost.AfterInitCallbacks to register custom logic after ServiceStack's AppHost has been initialized.
Not sure if it's applicable but at runtime you can "hi-jack Requests" in ServiceStack by registering a RawHttpHandler or a PreRequestFilter, e.g:
appHost.RawHttpHandlers.Add(httpReq =>
MyShouldHandleThisRoute(httpReq.PathInfo)
? new CustomActionHandler((req, res) => {
//Handle Route
});
: null);
Simple answer seems to be, no. The framework wasn't build to be a run-time plugable system.
You will have to make this architecture yourself on top of ServiceStack.
Routing solution
To make it route to these run-time loaded services/routes it is needed to make your own implementation.
The ServiceStack.HttpHandlerFactory checks if a route exist (one that is registered on init). so here is where you will have to start extending. The method GetHandlerForPathInfo checks if it can find the (service)route and otherwise return a NotFoundHandler or StaticFileHandler.
My solution consists of the following code:
string contentType;
var restPath = RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
//Added part
if (restPath == null)
restPath = AppHost.Instance.FindPluginServiceForRoute(httpMethod, pathInfo);
//End added part
if (restPath != null)
return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
technically speaking IAppHost.IServiceRoutes should be the one doing the routing. Probably in the future this will be extensible.
Resolving services
The second problem is resolving the services. After the route has been found and the right Message/Dto Type has been resolved. The IAppHost.ServiceController will attempt to find the right service and make it execute the message.
This class also has init functions which are called on startup to reflect all the services in servicestack. I didn't found a work around yet, but ill by working on it to make it possible in ServiceStack coming weeks.
Current version on nuget its not possible to make it work. I added some extensibility in servicestack to make it +- possible.
Ioc Solution out of the box
For ioc ServiceStack.Funq gives us a solution. Funq allows making child containers where you can register your ioc on. On resolve a child container will, if it can't resolve the interface, ask its parent to resolve it.
Container.CreateChildContainer()

Why is data access tightly coupled to the Service base in ServiceStack

I'm curious why the decision was made to couple the Service base class in ServiceStack to data access (via the Db property)? With web services it is very popular to use a Data Repository pattern to fetch the raw data from the database. These data repositories can be used by many services without having to call a service class.
For example, let's say I am supporting a large retail chain that operates across the nation. There are a number of settings that will differ across all stores like tax rates. Each call to one of the web services will need these settings for domain logic. In a repository pattern I would simply create a data access class whose sole responsibility is to return these settings. However in ServiceStack I am exposing these settings as a Service (which it needs to be as well). In my service call the first thing I end up doing is newing up the Setting service and using it inside my other service. Is this the intention? Since the services return an object I have to cast the result to the typed service result.
ServiceStack convenience ADO.NET IDbConnection Db property allows you to quickly create Database driven services (i.e. the most popular kind) without the overhead and boilerplate of creating a repository if preferred. As ServiceStack Services are already testable and the DTO pattern provides a clean endpoint agnostic Web Service interface, there's often not a lot of value in wrapping and proxying "one-off" data-access into a separate repository.
But at the same time there's nothing forcing you to use the base.Db property, (which has no effect if unused). The Unit Testing Example on the wiki shows an example of using either base.Db or Repository pattern:
public class SimpleService : Service
{
public IRockstarRepository RockstarRepository { get; set; }
public List<Rockstar> Get(FindRockstars request)
{
return request.Aged.HasValue
? Db.Select<Rockstar>(q => q.Age == request.Aged.Value)
: Db.Select<Rockstar>();
}
public RockstarStatus Get(GetStatus request)
{
var rockstar = RockstarRepository.GetByLastName(request.LastName);
if (rockstar == null)
throw HttpError.NotFound("'{0}' is no Rockstar".Fmt(request.LastName));
var status = new RockstarStatus
{
Alive = RockstarRepository.IsAlive(request.LastName)
}.PopulateWith(rockstar); //Populates with matching fields
return status;
}
}
Note: Returning an object or a strong-typed DTO response like RockstarStatus have the same effect in ServiceStack, so if preferred you can return a strong typed response and avoid any casting.

Resources