Nest: Feature request: dependency injection everywhere

Created on 24 Sep 2017  路  8Comments  路  Source: nestjs/nest

I'd like to use dependency injection in pipes, filters and global guards as well.

Usecase: business validation with AOP, separated to the business logic
For example: The UserAdminController receives a UserCreateCommand, which I can validate it with the class-validator in a pipe so it has to be filled with the right data, but I cannot use a service to check is there any user with the same email address.

Most helpful comment

Hi @peterreisz,
Since guards determine which route can be accessed, maybe it's even better place than interceptor to provide this type of logic. Referring to your question, yes, there's a reason. All of these 4 building blocks - pipes, filters, interceptors, and guards have their specific responsibilities. The interceptors and guards are combined with modules, while pipes and filters are running outside the module zone. The pipes task is to validate types, object structure or to map data based on some specific conditions. The pipe is not the proper place to select from the database or call any service. On the other hand, the interceptors shouldn't validate the object schema or just decorate the data. If the override is necessary, it must be caused because of the e.g. database call. Same guards, which in your case should take over the validation responsibility.

All 8 comments

Hi @peterreisz,
The global pipes, guards, filters and interceptors are loosely coupled from any module. They can't inject anything since they don't belong to anything. Use controller-scoped, method-scoped or param-scoped (supported only by pipes) instead. Referring to your use-case, you shouldn't use pipe for that. The middleware should be better in this case. If you prefer more declarative-way, use interceptor.

Hi @kamilmysliwiec,
Thanks, I've managed to use an interceptor for my usecase.
Controller/Method/Param scoped Guards and Interceptors can use dependency injections, but Pipes and Exception filters can not do that. Is there a reason for this?

Hi @peterreisz,
Since guards determine which route can be accessed, maybe it's even better place than interceptor to provide this type of logic. Referring to your question, yes, there's a reason. All of these 4 building blocks - pipes, filters, interceptors, and guards have their specific responsibilities. The interceptors and guards are combined with modules, while pipes and filters are running outside the module zone. The pipes task is to validate types, object structure or to map data based on some specific conditions. The pipe is not the proper place to select from the database or call any service. On the other hand, the interceptors shouldn't validate the object schema or just decorate the data. If the override is necessary, it must be caused because of the e.g. database call. Same guards, which in your case should take over the validation responsibility.

The pipes task is to validate types, object structure or to map data based on some specific conditions

The pipe is not the proper place to [...] or call any service"

Having a validator to validate types, object structure that is a service which needs to be injected to the pipe is a valid use case. This collides with philosophy stated above. How should one deal with this use case?

Same would go with a i18n service. I'm using a customized version of the ValidationPipe and I would like to implement a better error handling (including translating the error).

Not quite sure how to inject a TranslationService in such a case. It could definitely be a static class and not a service in my case, but it may be a valid scenario.

@RDeluxe see Global pipes header https://docs.nestjs.com/pipes

After reading several times the documentation, I feel that some things are missing.

The global pipes don't belong to any scope. They live outside modules, thus as a result - they can't inject dependencies.

Yet

Pipes, same as exception filters can be method-scoped, controller-scoped and global-scoped. Additionally, a pipe may be param-scoped

After reading this, I had the feeling that I could create controller-scoped pipes and use dependency injection, but it does not seem possible (not a good choice in my case anyway).

I'm a bit lost here. My use case is simple: use class-transformer and class-validator to cast a JSON to a class instance, and throw an error if the JSON does not validate the class schema. However I need to log this (using a logger service) and to throw translated errors (based on the user language) in case of error (TranslatorService + access to connected user information).

I do not want to use the pipe for all my routes, I'm not using useGlobalPipes.

Moreover, I did not find any example of pipe registration in a module, which should/could be possible if I'm reading this correctly :

Let's assume that we have a ValidationPipe registered in the SharedModule. This SharedModule is imported into root module. We can pick the ValidationPipe instance using following syntax:

I may just switch to an interceptor instead of a pipe, it seems to cover more or less the same use cases, and can inject dependencies (such as my loggerService and translationService).

Edit: The problem with interceptors is that they don't receive the metatype like the pipes do.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rafal-rudnicki picture rafal-rudnicki  路  3Comments

rlesniak picture rlesniak  路  3Comments

FranciZ picture FranciZ  路  3Comments

JulianBiermann picture JulianBiermann  路  3Comments

janckerchen picture janckerchen  路  3Comments