Как достать метод и метаданные из провайдера в NestJS?
Пишу обёртку NestJS обёртку для Typegoose так как существующая больше не поддерживается и там есть один критический недостаток, который я хочу устранить в своей реализации.
Задача: есть декоратор @EventTrackerFor(schema: AnyClass), который принимает в себя класс Typegoose. Под капотом это работает так:
export const EventTrackerFor = (schema: AnyClass) =>
applyDecorators(Injectable, SetMetadata('tracker-for', schema.name));
А также декораторы @Pre(eventName: PreEvents) и @Post(eventName: PostEvents), которые реализованы так:
export const Post = (eventName: PreEvents) => SetMetadata('post', eventName);
export const Pre = (eventName: PostEvents) => SetMetadata('pre', eventName);
В итоге у пользователя библиотеки это будет выглядеть так:
@EventTrackerFor(User)
class UserEventTracker {
constructor(private readonly anyService: AnyService) {}
@Pre(PreEvents.SAVE)
@Post(PostEvents.SAVE)
logOnAndAfterCreate() {
console.log('user created')
}
}
// ------------------------ Любой модуль
@Module({
imports: [MyModule.forFeature([ {schema: User} ])],
providers: [UserEventTracker]
})
class AnyModule {}
Необходимо как-то выцепить значение из декоратора @EventTrackerFor(), а также методы этого провайдера, которые помечены декораторами @Pre() и @Post(), включая значения, переданные в эти декораторы.
Пытался подглядеть реализацию этого поведения в различных пакетах, например таких, как @nestjs/bull, но там везде так много кода, что я так и не смог понять, как же они всё-таки это делают.
Репозиторий проекта: https://github.com/GrapeoffJS/kindagoose
Ответы (1 шт):
Чтобы получить всех провайдеров в NestJS есть DiscoveryModule. Для получения метаданных из провайдеров необходим будет Reflector и MetadataScanner.
Пример как этим пользоваться ниже:
import { DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core';
@Injectable()
class MetadataExplorer {
constructor(
private readonly discoveryService: DiscoveryService, // экспортируется из DiscoveryModule
private readonly reflector: Reflector,
private readonly metadataScanner: MetadataScanner,
) {}
onModuleInit() {
const allProviders = this.discoveryService.getProviders(); // Список всех провайдеров в приложении
allProviders.forEach(({ instance }) => {
const classMetadata = this.reflector.get('your_metadata_key', instance.constructor); // Вернёт все метаданные класса по ключу
this.metadataScanner.scanFromPrototype( // Находит методы класса, которые имеют метаданные
instance,
Object.getPrototypeOf(instance),
(methodName) => {
const methodMetadata = this.reflector.get('your_metadata_key', instance[methodName]); // Вернёт все метаданные метода в классе по ключу
},
);
});
}
}
Ну и нужно не забыть добавить этот класс в провайдеры вашего модуля