Trying to figure out the best approach.
1) Sequelize section in NestJS docs shows the following way:
const catsProviders = [
{
provide: 'CATS_REPOSITORY',
useValue: Cat,
}
And then in service
#Inject('CATS_REPOSITORY') private readonly catsRepository: typeof Cat
2) And recently I found in Nest github Sequelize sample:
#InjectModel(User) private readonly userModel: typeof User,
Is there any difference - at least for Sequelize - in term of perfomance or memory usage?
The first approach is way more verbose: you also need to create a constants file for tokens.
But at least having a predefined token means that we use the same provider instance in different parts of application?
But maybe InjectModel does the same under the hood ?
#InjectModel() is a wrapper around #Inject(getModelToken()) which ends up returning a token like connectionPrefixEntityRepository (this is the same functionality as #nestjs/typeorm's #InjectRepository() actually). So there is no performance difference between #InjectModel(Entity) and #Inject(CONST_TOKEN) as they are essentially the same.
Related
I used loopback 4 to bootstrap my API application and developed some parts of it.
Now I'm trying to access repositories outside the controllers and tried everything I could but didn't help. None of my searches did too. For example I have a controller in which I can access repo such way.
constructor(
#repository(UserRepository) public userRepository: UserRepository){}
But if it isn't a controller it won't work and I found out I had a wrong understanding about #repository and #inject decorators.
My Case:
I want to run a cron-job to perform an update operation on database every day at a specific time.
The thing is I think I should create a service or something like that to expose database operations so it can be accessible anywhere.
The issue you're trying to tackle comes down to dependency injection. It's described at https://loopback.io/doc/en/lb4/Dependency-injection.html but that article does not explain how to create a separate class that works with dependency injection.
In short, in order to perform the dependency injection through decorators, LoopBack needs to be the one to construct the class you're writing, so that it can pick what you need from its Context. So, instead of writing new ClassSomething, it has to be #inject(ClassSomething) or #service(ClassSomething), and those decorators only work in a class also instantiated by LoopBack. In other words, your class has to be in the "call tree" from the original application class (application.ts) for dependency injection to work.
The easiest way to do what you want to do is to use LoopBack's Cron component: see https://loopback.io/doc/en/lb4/Running-cron-jobs.html. You could convert your class to a CronJob if it has no other purpose, or let the CronJob create an instance of your class. In the latter case, you need to bind your class to the application Context so that the CronJob can access it through decorators. You can do so in two ways:
Convert your class to a Service (see https://loopback.io/doc/en/lb4/Service.html and https://loopback.io/doc/en/lb4/Service-generator.html). If what your class does can be thought of as a service, this is the way to go.
Inject your class with: #service(MyServiceClass) myService: MyServiceClass
Directly bind your class to the application context, in application.ts:
this.bind('MyClass').toClass(MyClass);
Inject your class with: #inject(MyClass) myClass: MyClass
The latter option is not best practice AFAIU as it does not adhere to IoC-principles (https://en.wikipedia.org/wiki/Inversion_of_control) - basically, by hard-coding the class binding in the application class, it is not exactly modular. When your class is converted to a service (and placed in src/services), it will automatically be added to the Context, meaning you can inject it everywhere using #service().
I also experience the same(cron word). I couldn't find any solution still in the documentation. But still, I have achieved this in this way.
just call repository as like class with dependency in it. do this in your index.ts file where you start the application.
async function startCronJobs(app: SawLoopbackApplication) {
const userRepo = app.repository(UserRepository);
const userRepoInstance = await userRepo.getValue(app);
const cron = new CronComponent(userInstance);
//---- Exicute your cron here
new CronJob('0 6 * * *', function () {
cron.sendMorningMail()
}).start();
//---- Exicute your Second Cron
new CronJob('0 8 * * 1', function () {
cron.weeklyAppraisalsToAgent()
}).start();
}
call your cron in the functional component and execute a raw query to get the details from DB etc...
import {MysqlDataSource} from '../../datasources'
const query = `select * from accounts where IP="${ipAddress}" ORDER By
createdAt DESC Limit 1`
const dataSource = new MysqlDataSource()
const accountDetails = await dataSource.execute(query)
console.log(accountDetails)
instead of a raw query. call your repository from the function component below
import {MysqlDataSource} from '../datasources'
import {ContactUsUserDetailsRepository} from '../repositories'
const contactUsUserDetailsRepository = new ContactUsUserDetailsRepository(new MysqlDataSource)
function saveContactDetails(){
const payload={UID:"Unique ID",name:"Durai"}
await contactUsUserDetailsRepository.create(payload)
}
I would prefer 2 because, if you have a lot of imports in your cron class constractor you need to pass all of them while calling a function like in the 3rd point.
Have fun:)
In the Nestjs documentation, they suggest using their Test module for instantiating any objects used for testing, like so:
const adoptionServiceMock = {
adoptCat: jest.fn()
};
const moduleRef = await Test.createTestingModule({
providers: [CatsService, { provider: AdoptionService, useValue: adoptionServiceMock }],
}).compile();
catsService = moduleRef.get<CatsService>(CatsService);
What is the benefit of doing it this way, as opposed to regular instantiation? Here's an example of the regular way:
const adoptionServiceMock = {
adoptCat: jest.fn()
} as AdoptionService;
catsService = new CatsService(adoptionService);
I'm specifically referring to using catsService = moduleRef.get<CatsService>(CatsService) instead of newing up a CatsService.
The one thing I can think of is that it does object instantiation the same way Nestjs would do it in production - with DI. If you forget to add Inject() on CatsService or AdoptionService, the test will fail.
If you don't find benefit in it, you absolutely can use whatever method you prefer for mocking. The Test.createTestingModule is there to provide uniformity between Nest projects and to provide an easy way to give mocks to the classes without having to use as whatever, but if you prefer that method, then go for it.
It becomes more powerful out at the e2e/integration level where you can mock guards and other enhancers with fluent methods, but again, the final test implementation is absolutely up to you.
I personally like it cause I can delegate Nest to create providers based off of a class and mock things from there, like a TypeORM repository can be created as
{
provide: getRepositoryToken(EntityClass),
useClass: Repository,
}
No need to extensively create a lot of methods, then later I can use jest.spyOn() or jest.fn() to override the default functionality.
I Can take instance of service using this example:
export class GetEntityDomainService {
constructor(private readonly moduleRef: ModuleRef) { }
getEntity(): void {
const myObject = this.moduleRef.get(MyClassName);
}
}
but how could I invoke a service instance outside of a class object
where I don't have a handle to moduleRef:
here is example from angular:
const injector = ReflectiveInjector.resolveAndCreate(providers);
const widgets: WidgetService = injector.get(WidgetService);
https://kevinphelps.me/blog/2017-01-17-using-angular-dependency-injection-in-node
whether is it possible to download an instance of the service without needing a moduleRef?
Thanks
Piotr
There are several reasons
sometimes I need to inject a service in a function, then I have to pass moduleRef to this function to make it possible
The second example is Command and Events -
for example I create such an action, then I pass parameters to send to handler,:
this.dispach(new Command (parameters ...))
Currently my actions know from which module they are triggered and in which module their handler is - so I won't send actions between wrong areas of the system (layers, dependencies). I check all this when executing
this.dispach (new Command ())
now I have to write such a dispatch as follows:
this.dispach (new Command (this.moduleRef, ... other parameters))
could I create a moduleRef from the code?, it would facilitate the process of creating an action and eliminate the need to pass moduleRef
but I don't know if it will help me
as I analyzed DI nest, it works differently from angular
The service knows where it was declared and when it injects something in it, it uses the tokens declared in this module
I suppose I will only be able to inject general scope
I have the follwoing code from tutorial|:
constructor(#InjectModel('User') private readonly userModel: Model<User>) {}
Where User is:
export interface User extends Document {
readonly name: string;
readonly age: number;
readonly phone: string;
}
Could you explain how #InjectModel works, what is 'User' and why we passed Model<User>, what does it mean?
What I can inject also using #InjectModel?
All right, to get into this, first we have to take to truth that interfaces do not exist at runtime. So the User interface you have is only useful during development. I'll try to break this down step by step, starting from the end of the line and working backwards.
Model<User>: Model is an interface type exposed by mongoose that allows us to know that the model we're using has methods like find and create. By saying Model<User> we are saying "This is a mongoose model object that refers to the User interface. This is especially useful for Typescript because as the functions are typed with generics, it knows what the return of methods like find are: an array of User objects. The model interface is really Model<T> where T is the interface that extends Document (another mongoose type).
What is 'User': 'User' is the string equivalent of the name of the interface. If your interface that extends Document is called Dog you use 'Dog', if it's Animal you use 'Animal'. The reason for not passing the interface is because interfaces do not exist at runtime (unlike classes).
How does #InjectModel() work: Okay, the really fun part of the question to answer. Nest works normally by using Injection Tokens. Normally, these tokens are determined by the type of the injected value. In your case Model<User>. Now, the problem here is that A) interfaces don't exist at runtime and B) Typescript does not reflect generics well, so even if Model was a class, all that could be gotten would be Model which isn't enough information on what to inject. So the next logical step Nest takes is to allow a user to provide injection tokens and use the #Inject() decorator. You can do things like injecting an object this way (like package configuration information). Useful, but a bit hard to work with without building your own providers. Now steps in #InjectModel(). #InjectModel() builds an injection token based on the string that's passed into the function. This token is something along the lines of typeModel where type is actually what you pass into the function. This tells Nest specifically what model we are injecting. This also needs to align with the provider created with MongooseModule.forFeature(), hence why name and the value passed to #InjectModel() need to be aligned. Usually it's easiest to align when they use the same string name as the interface.
Is there some high level ORM for nodejs datastore client library? Because it becoming really hard to maintain relatively small application when entities referencing and objects versioning takes place. Just interested, should I start write my own bicycle or someone already wrote something like that?
If not, maybe there some separate libraries to implemented appropriate referencing mechanism?
Check out gstore-node:
gstore-node is a Google Datastore entities modeling library for Node.js inspired by Mongoose and built on top of the #google-cloud/datastore library.
It is not a replacement of #google-cloud/datastore but a tool built to help modeling Entities through Schemas and to help validating the data saved in the Datastore.
You may try on this one, very well structure, written in Typescript.
https://www.npmjs.com/package/ts-datastore-orm
import {BaseEntity, Column, Entity} from "ts-datastore-orm";
#Entity({kind: "user"})
export class User extends BaseEntity {
#Column({generateId: true})
public id: number = 0;
#Column({index: true})
public total: number = 0;
}
async function main() {
const user = User.create();
await user.save();
const id = user.id;
}