简体   繁体   中英

Nest.js configurable middleware dependency injection

There are plenty of articles showing how to inject providers into dynamic module but all of them only show examples using their exported services, none with middleware. Using the exact same method as used with services for middleware fails. How can I create reusable middleware that requires providers to be injected?

Example middleware, requiring injection of a UserService provider

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(@Inject(USER_SERVICE) private readonly userService: UserService) {
    console.log('userService findAll: ', userService.findAll());
  }

  use(req: any, res: any, next: () => void) {
    console.log('AuthMiddleware called!');

    next();
  }
}

Example module containing the middleware:

@Module({
  providers: [AuthService, AuthMiddleware],
  imports: [ExtModule],
})
export class AuthModule extends createConfigurableDynamicRootModule<
  AuthModule,
  AuthModuleOptions
>(AUTH_OPTIONS, {
  providers: [
    {
      provide: AUTH_SECRET,
      inject: [AUTH_OPTIONS],
      useFactory: (options: AuthModuleOptions) => options.secret,
    },
    {
      provide: USER_SERVICE,
      inject: [AUTH_OPTIONS],
      useFactory: (options: AuthModuleOptions) => options.userService,
    },
  ],
  controllers: [AuthController],
}) {}

Now importing the module and trying to use the middleware:

@Module({
  imports: [
    ConfigModule.forRoot(),
    AuthModule.forRootAsync(AuthModule, {
      imports: [ConfigModule, UserModule],
      inject: [ConfigService, UserService],
      useFactory: (config: ConfigService, userService: UserService) => {
        return {
          secret: config.get('AUTH_SECRET_VALUE'),
          userService,
        };
      },
    }) as DynamicModule,
    UserModule,
  ],
  controllers: [UserController],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AuthMiddleware).forRoutes('*');
  }
}

Now when initializing the AuthModule dependencies the middleware class is clearly instantiated correctly, the userService.findAll being logged:

userService findAll:  []

So far so good, and this works fine for services . But the problem is that when then actually using the middleware in configure() , the injected context is not used

...\app\node_modules\@nestjs\core\injector\injector.js:193
            throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
                  ^
Error: Nest can't resolve dependencies of the class AuthMiddleware {
    constructor(userService) {
        this.userService = userService;
        console.log('userService findAll: ', userService.findAll());
    }
    use(req, res, next) {
        console.log('AuthMiddleware called!');
        next();
    }
} (?). Please make sure that the argument Symbol(USER_SERVICE) at index [0] is available in the AppModule context.

I've ended up getting it to work, mostly by re-exporting the injected providers. After trying different combinations, the working one is as follows

  • Static dependencies (eg external libraries) need to be imported and re-exported inside the module decorator
  • Dynamic dependencies need to be imported and re-exported inside createConfigurableDynamicRootModule
  • Exporting the Middleware class seems to have no effect in any way
@Module({
  providers: [],
  imports: [ExtModule],
  exports: [ExtModule],
})
export class AuthModule extends createConfigurableDynamicRootModule<
  AuthModule,
  AuthModuleOptions
>(AUTH_OPTIONS, {
  providers: [
    {
      provide: AUTH_SECRET,
      inject: [AUTH_OPTIONS],
      useFactory: (options: AuthModuleOptions) => options.secret,
    },
    {
      provide: USER_SERVICE,
      inject: [AUTH_OPTIONS],
      useFactory: (options: AuthModuleOptions) => options.userService,
    },
  ],
  exports: [
    {
      provide: AUTH_SECRET,
      inject: [AUTH_OPTIONS],
      useFactory: (options: AuthModuleOptions) => options.secret,
    },
    {
      provide: USER_SERVICE,
      inject: [AUTH_OPTIONS],
      useFactory: (options: AuthModuleOptions) => options.userService,
    },
  ],
}) {}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM