NestJS. Why decorator Global not working? - node.js

I have 2 modules. One (payment) is responsible for payments. The second (auth) should send a verification request to a third-party server.
I wanted to make the (auth) module global so that I can freely inject its methods and not use "export" and "import" for my "providers".
But for some reason this doesn't work, the #Global decorator seems to be simply ignored.
What am I doing wrong?
Example on codebox: https://codesandbox.io/s/nest-forked-gsy160?file=/src/auth/auth.module.ts

You still need to use exports to allow access to the module's public providers. Once you add in exports this will work fine.
You can think of a module like a container with its private API (non-exported providers), public API (exported providers), entrypoints (controllers, resolvers, gateways) and configurations (imports). You need to export the providers to make them public and available. The #Global() will just mean you don't need to add the AuthModule to other module's imports (besides your root module)

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.

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.

Environment specific configuration of datasources in loopback4 application

I have just started my first loopback project and chosen loopback4 version for the application. Its purely a server application which will interact with databases (Redis and mongodb) and will call external API services due to micro-service architecture.
Now, I have 3 datasources in my application i.e. mongodb, Redis, and REST based datasource to call external services. I am facing 2 problems in going forward.
1. Environment specific configurations of Datasources: I need to maintain configuration for all three datasources according to the NODE_ENV environment variable. For lb3 i found this solution,
https://loopback.io/doc/en/lb3/Environment-specific-configuration.html#data-source-configuration
which does not work in lb4. One solution is to add configuration files having names mongodb.staging.json and mongodb.production.json and same for redis and rest datasources in directory src/datasources, and load this config according to NODE_ENV variable using if condition and pass it to the constructor of datasource. It works but it does not seem nice, as it should be application's responsibility to do this.
Can somebody suggest me lb3 equivalent solution for the above?
2. Calling External APIs via datasource: in lb4, To call external services its recommended to have a separate REST based datasource and its service to call it via controller. Now, In REST datasource config, one has to define a template of all the API calls which will happen to the external service https://loopback.io/doc/en/lb4/REST-connector.html#defining-a-custom-method-using-a-template.
As my application calls external service heavily with relatively large number of request parameters. It becomes really messy to declare each API call with its request params and to maintain this in the datasource config which will be environment specific.
Can somebody tell me a more robust and cleaner alternative of the above problem?
Thanks in advance!!
Using environment variables in datasource configs
The datasource config is simply a JSON file that's imported in into *.datasource.ts. Hence, you can replace that JSON file with a Typescript file and import it accordingly. LoopBack 4 does not provide any custom variable substitution mechanism. Instead, it is recommended to use process.env.
Recent CLI versions replace the JSON config in favour of using a single Typescript file:
import {inject} from '#loopback/core';
import {juggler} from '#loopback/repository';
const config = {
name: 'db',
connector: 'memory',
};
export class DbDataSource extends juggler.DataSource {
static dataSourceName = 'db';
static readonly defaultConfig = config;
constructor(
#inject('datasources.config.db', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}
The dependency injection in the constructor allows you to override the config programmatically via the IoC container of the application.
Further reading
https://loopback.io/doc/en/lb4/DataSources.html
Calling external APIs without REST connector
The REST connector enforces a well-defined interface for querying external APIs so as to be able to do validation before sending out the request.
If this is not favourable, it is possible to create a new Service as a wrapper to the HTTP queries. From there, you can expose your own functions to handle requests to an external API. As Services do not need to follow a rigid structure, it is possible to customize it to your use-case.
It is also possible to create a new request directly inside the controller using either built-in or external libraries.
Overall, there isn't a 100% right or wrong way of doing certain things in LoopBack 4. Hence why the framework provides numerous ways to tackle the same issue.

How to use NESTJS modules in separate classes

Sometimes I need to use some methods which implemented in nestjs code structure in separate from this classes
For example we have such architecture:
entities
-entity.entity.ts
-entity.module.ts
-entity.service.ts
-entity.providers.ts
So how Can I use method or property from entity.service in separate class? Such like this:
import EntityService from './entities.entity.service'
export class SeparateClass{
propertyFromEntityService: string
constructor() {
this.propertyFromEntityService = EntityService.propertyFromEntityService
}
}
And one more important point. I don’t want to implement this separate class in the nestjs structure, I just want to use it as a regular class. Thank you
If you are not wanting to use dependency injection via Nest, but you still want to use a property from a class in the Nest application, you'll need to manually instantiate the class, providing whatever dependencies that class has, and then pulling the property from the service. The other option, if this class will be used outside of the Nest context, but still while the Nest application is running, is you can use the Nest application to get the service via app.get(EntityService), but this does require you to have access to app after the NestFactory has created the application.

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.

Resources