I am using Nest JS v8 and Type ORM v8.1.2. I want to create custom repository such as following sample and add custom methods to it. but the version of Type ORM doesn't support this feature.
#EntityRepository(User)
export class UserRepository extends Repository<User>
customFind() {}
}
Tip: Actually i want make a generic repository.
How can i implementing it?
Thanks for helping :)
Typeorm current version doesn't support this feature, you can try this method to create your custom repository
#Injectable()
export class UserRepository {
constructor(
#InjectRepository(UserEntity)
private readonly repository: BaseRepository<UserEntity>,
) {}
get instance() {
return this.repository;
}
}
at ^0.2.45 version
#EntityRepository(UserEntity)
export class UserRepository extends BaseRepository<UserEntity> {}
note: i'm using BaseRepository in typeorm-transactional-cls-hooked
Yes that can be done. With custom repositories, the DI is not managed by Nestjs. I learned that the hard way. The trick is to use getCustomRepository.
This is how you would do it
#EntityRepository(SomeEntity)
export class SomeCustomRepository extends Repository<SomeEntity> {
customFind() {}
}
#Injectable()
export class SomeService {
async doStuff() {
const repository = getCustomRepository(
SomeCustomRepository
);
repository.doStuff()
}
}
Related
I have a bunch of services defined in my NestJS project. Some of the services are used as common services in a lot of other services. So they are imported in a few modules as well. But I noticed that when specific service srvD is imported in another service srvE2, the onModuleInit is not being called when the project starts.
The project starts without any error. Not really sure what's happening.
An example of the project structure. Issue is in srvE2
srvA.ts
#Injectable()
export class SrvA {
constructor(
private somePkgSrv: SomePkgSrv,
) {}
}
srvB.ts
#Injectable()
export class SrvB {
constructor(
private srvA: SrvA,
) {}
}
srvC.ts
#Injectable()
export class SrvC {
constructor(
private srvA: SrvA,
private srvB: SrvB,
) {}
}
srvD.ts
#Injectable()
export class SrvD {
constructor(
private srvA: SrvA,
private srvB: SrvB,
private srvC: SrvC,
) {}
}
srvD.module.ts
#Module({
providers: [SrvA, SrvB, SrvC, SrvD],
exports: [SrvD],
})
srvE1.ts
export class SrvE1 implements OnModuleInit {
constructor(
private srvA: SrvA,
private srvB: SrvB,
private srvC: SrvC,
) {}
async onModuleInit() {
console.log ('I can print! Yay!')
}
}
srvE2.ts
export class SrvE2 implements OnModuleInit {
constructor(
private srvA: SrvA,
private srvB: SrvB,
private srvC: SrvC,
private srvD: SrvD,
) {}
async onModuleInit() {
console.log ('I refuse to print so long as SrvD is here. Comment it and I will
print')
}
}
srvE.module.ts
#Module({
import: [SrvD], // the module
providers: [SrvE1, SrvE2], // the services
exports: [SrvE1, SrvE2],
})
I am dealing with a similar issue. Based on the comments here I tried changing the scoping for my new dependency from Scope.REQUEST to Scope.DEFAULT and that resolved my issue.
Request scoped providers spread this scope to whole chain of injection. Consequently, if you use it, even deeply inside your code, then onModuleInit won't run.
That being said, well ... request scope isn't very recommended, performance wise. Here is an interesting article : Why You Should Avoid Using Request-scoped Injection in NestJS
I've also bumped into this question & its answers : How to inject a request scoped provider at NestJS controller?
I've tried nj-request-scope package from #profes, and it works fine, especially to fix your problem.
If you don't want to use it, you can still try to move your onModuleInit code in a default scoped service.
In angular we can manually access and inject dependencies using the built in Injector class. By which you can access Injectables and Inject them without actually passing them in the constructor. Basically I want to inject a service to another service without passing it as an arg to the constructor.
This is the angular equivalent Inject a service manually
I wanted to achieve similar thing in nestjs
Note : The service to be injected also has a dependency, so I can't just instantiate it
I believe what you're looking for is Nest's ModuleRef class, where you can do something like the following:
#Injectable()
export class CatsService implements OnModuleInit {
private service: Service;
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
this.service = this.moduleRef.get(Service);
}
}
Where Service should actually be the class you are wanting to inject.
I think #Inject('token') should work as you expected
import { Injectable, Inject } from '#nestjs/common';
#Injectable()
export class HttpService<T> {
#Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
ref: https://docs.nestjs.com/providers#property-based-injection
I am working with Nest.js + TypeORM and hit a snag when trying to add inheritance to service classes.
I want to have a User service class that extends off of a Base service class, inheriting all the methods the it has.
This is what I've done:
export class BaseService<T> {
private repo;
constructor(repo: Repository<T>){
this.repo = repo;
}
async findAll(opts?): Promise<T[]> {
return this.repo.find(opts);
}
......
}
Then on my User service:
export class UserService extends BaseService<User> {
constructor(
#InjectRepository(User)
private userRepository: Repository<User>,
private readonly mailerService: MailerService,
) {
super(userRepository);
}
}
This works fine where I just need a single repository in the Service class but once I need more such as productRepository, as you can see it would fail due to constructor being hardcoded to accept a single repository.
I can't seem to figure out what would be the most elegant way of achieving something like this.
Does anyone know?
It is an old thread, anyways I meat this same problem and this answer may help someone else...
You can have an abstract class with common functions and boilerplate that you wich to be available in all your repositories to maintain patterns and so on...
export abstract class BaseService<T extends ObjectLiteral> {
protected repo: Repository<T>;
async findAll(opts?): Promise<T[]> {
return this.repo.find(opts);
}
......
}
Observe that constructor is omitted here and as an abstract class you should never have an instance of it, it is meant to be just an extension with common boilerplates and features that you don't want to write every single time you need a new service with the same features like a CRUD service or so...
Then you will use it like this:
export class UserService extends BaseService<User> {
constructor(
#InjectRepository(User) protected readonly repo: Repository<User>,
private readonly mailerService: MailerService,
) {
super();
}
...... extended stuff
}
I'm not sure if you need to inject repo with the same name and visibility but as it worked for me I didn't dive any further to get to know...
The bad side of this approach to me is that even if you don't have anything else to inject via constructor in your service you need to declare constructor and inject the base repository... it is a boilerplate that could be avoided too but I couldn't achieve this right now...
I need to access a service (provided by Nest TypeOrmModule) inside the intercept function (important note: not as constructor parameter!!!) because it depends of the passed options (entity in this case).
The service injection token is provided by the getRepositoryToken function.
export class PaginationInterceptor {
constructor(private readonly entity: Function) {}
intercept(context: ExecutionContext, call$: Observable<any>): Observable<any> {
// Here I want to inject the TypeORM repository.
// I know how to get the injection token, but not HOW to
// get the "injector" itself.
const repository = getRepositoryToken(this.entity);
// stuff...
return call$;
}
}
Is any concept of "service container" in Nest? This github issue didn't help me.
Example usage (controller action):
#Get()
#UseInterceptors(new PaginationInterceptor(Customer))
async getAll() {
// stuff...
}
Regarding dependency injection (if you really want/need it), I guess using a mixin class can do the trick. See the v4 documentation (Advanced > Mixin classes).
import { NestInterceptor, ExecutionContext, mixin, Inject } from '#nestjs/common';
import { getRepositoryToken } from '#nestjs/typeorm';
import { Observable } from 'rxjs';
import { Repository } from 'typeorm';
export function mixinPaginationInterceptor<T extends new (...args: any[]) => any>(entityClass: T) {
// declare the class here as we can't give it "as-is" to `mixin` because of the decorator in its constructor
class PaginationInterceptor implements NestInterceptor {
constructor(#Inject(getRepositoryToken(entityClass)) private readonly repository: Repository<T>) {}
intercept(context: ExecutionContext, $call: Observable<any>) {
// do your things with `this.repository`
return $call;
}
}
return mixin(PaginationInterceptor);
}
Disclaimer: this is valid TypeScript code but I didn't had the chance to test it in a real project, so it might need a bit of rework. The idea is to use it like this:
#UseInterceptors(mixinPaginationInterceptor(YourEntityClass))
Let me know if you have any question about the code. But I think the doc about mixin is pretty good!
OR You can also use getRepository from typeorm (passing the entity class). This is not DI, thus, it will oblige you to spyOn the getRepository function in order to do proper testing.
Regarding the container, I'm almost sure that the only way to access it is using the Execution Context, as pointed by Kim.
I want to create an instance of a dynamically loaded class trough Nest.js dependency injection service.
In Angular I would use Injector.create, what would be the equivalent in Nest.js ?
First of all you should get a ModuleRef which references current module, and then use its "get" method to get an instance.
#Injectable()
export class AppletService {
files: FileService;
constructor(
private moduleRef: ModuleRef,
) {
this.files = moduleRef.get(FileService);
}
}