Rafa Acioly
Rafa Acioly

Reputation: 636

How to inject service into middleware using NestJS?

I've a middleware to validate a user quota and I need to inject the service that call the database into it to check this quota.

This is the middleware:

export class PointMaxStorageMiddleware implements NestMiddleware {

    constructor(private readonly points: PointsService) {}

    async use(req: Request, res: Response, next: NextFunction) {
        const total = await this.points.count('user-id-test');
        if (total >= LIMIT) throw new HttpException('error sample', 400);
        // ...
    }
}

The service class:

@Injectable()
export class PointsService {
    private readonly logger = new Logger(PointsService.name);

    constructor(
        @InjectModel(Point.name)
        private readonly collection: Model<PointDocument>,
    ) {}

    async count(userId: string): Promise<number> {
        return await this.collection.countDocuments({ userId: userId });
    }
}

My service class has the Injectable annotation and is being used on other parts of the project, but when I inject it into the middleware, I got the error:

Cannot read properties of undefined (reading 'count')

The count method exist on the service.

I also tried to use like this:

constructor(
    @Inject(PointsService) private readonly points: PointsService,
) {}

And got the erro:

Nest can't resolve dependencies of the PointsService (?). Please make sure that the argument PointModel at index [0] is available in the AppModule context. Potential solutions:

  • If PointModel is a provider, is it part of the current AppModule?
  • If PointModel is exported from a separate @Module, is that > module imported within AppModule? @Module({ imports: [ /* the Module containing PointModel */ ]}) Error: Nest can't resolve dependencies of the PointsService (?). Please make sure that the argument PointModel at index [0] is available in the AppModule context.

Also tried to add a new provider on the AppModule class:

providers: [
    {
        provide: 'POINT_SERVICE',
        useClass: PointsService,
    },
]
constructor(
    @Inject('POINT_SERVICE') private readonly points: PointsService,
) {}

And got the same error as above.

The injection is not being placed correctly, how can I inject a service into a middleware?

Upvotes: 0

Views: 5691

Answers (3)

Farista Latuconsina
Farista Latuconsina

Reputation: 353

As Manuel Herrera answer mentioned before that you must set the middleware up using the configure(). But, remember that in NestJS if you want to share providers across the module, you have to import the source / the hosting module, not just provide the specified provider you want to use. In this case, if you want to share or provide the PointsService at the middleware module. It would be something like this:

@Module({
  imports: [PointsModule], // this is the module where you provide the PointsService
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(PointMaxStorageMiddleware)
      .forRoutes('myroute');
  }
}

You can take a look at the dependency injection section inside the NestJS middleware documentation here.

Upvotes: 0

Manuel Herrera
Manuel Herrera

Reputation: 351

Nest cant resolve dependencies for an injectable that are outside its parent module

Remember NestMiddleware is also an @injectable(). This means first you define it, and then you must set it up using the configure() method in a module (as the @module decorator has no place for middleware) like so:

@Module({
  providers: [PointsService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(PointMaxStorageMiddleware)
      .forRoutes('myroute');
  }
}

note that PointsService should be in the same module, be that through providers or through imports (if exported from another module)

check https://docs.nestjs.com/middleware for more information

Upvotes: 5

Micael Levi
Micael Levi

Reputation: 6695

You should have the @Injectable() on classes that you want inject things into.

Since you want to inject PointsService in the class PointMaxStorageMiddleware, you need to annotate PointMaxStorageMiddleware with that decorator.

Another approach would be:
@Inject(PointsService) private readonly points: PointsService

Upvotes: 2

Related Questions