In nestjs, I understand that
imports: [
TypeOrmModule.forFeature([TaskRepository]),
],
creates some kind of provider token so then I can inject the repository in my service (in same module) with this token.
What I don't understand is how @InjectRepository()
works internally, for example:
constructor(
@InjectRepository(TaskRepository)
private taskRepository: TaskRepository,
) {}
How is InjectRepository
custom decorator (which should have been created with createParamDecorator
) able to inject a provider registered in a module? I researched and found out that createParamDecorator
doesn't have ability to use DI. If createParamDecorator
just creates a new TaskRepository
instance and returns it, then why do I need to import TypeOrmModule.forFeature
in module? Can someone explain how this works internally? Thanks!
@InjectRepository() on the other hand is an extension of @Inject() that takes the currently passed entity/repository and uses some logic to create a new injection token. Generally, this logic is <EntityName>Repository sometimes with a connection added to it.
Dependency injection is an inversion of control (IoC) technique wherein you delegate instantiation of dependencies to the IoC container (in our case, the NestJS runtime system), instead of doing it in your own code imperatively.
TypeORM Integration. For integrating with SQL and NoSQL databases, Nest provides the @nestjs/typeorm package. Nest uses TypeORM because it's the most mature Object Relational Mapper (ORM) available for TypeScript. Since it's written in TypeScript, it integrates well with the Nest framework.
You're on the right track, but @InjectRespoitory()
and createParamDecorator()
are generally two different decorator types. Yes they both work on parameters, but they generally have different use cases.
createParamDecorator
works on controller, resolver, and gateway route handlers, so that Nest can know what part of the req
, context
, or payload
object (or whatever other part of the ExecutionContext
you want to get) should be put into the route handler's parameter position. The code behind that is a little complex, but I can point you to it if you get really curious.
@InjectRepository()
on the other hand is an extension of @Inject()
that takes the currently passed entity/repository and uses some logic to create a new injection token. Generally, this logic is <EntityName>Repository
sometimes with a connection
added to it. This token matches up with the created token from TypeormModule.forFeature()
for the same entity. (Side note: if the entity passed is a repository, the token is just <EntityName>
with the optional connection if it is not the default one).
You can try this out but getting rid of @InjectRepository()
and instead using @Inject()
just for testing purposes, but I wouldn't suggest that in a production server instance. Too many chances it will go wrong.
The really important thing to make note of here is that @InjectRepository()
doesn't actually inject anything. It just sets up the metadata for Nest to read and match to a custom provider. TypeormModule.forFeature()
is actually what's doing the heavy lifting here, creating a custom provider with a matching injection token and providing the right value for the provider.
By the way, @InjectRepository()
and TypeormModule.forFeature()
have to work together because Typescript cannot reflect generics properly. Normally with Nest we can use class names as injection tokens, but something like Repository<TaskEntity>
just comes back as Repository
so Nest wouldn't know which repository to inject. By using the @InjectRepository()
decorator we are able to set the injection token metadata and override the reflected class value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With