This package will help you make your services clean and focused on the feature. Many non-functional requirements can be easily added using Aspect-Oriented Programming.
Aspect-oriented programming with NestJS
$ npm i @nestjs-architects/aop
Create your own advice (additional piece of code executed along with the original method)
import { AdviceProvider } from '@nestjs-architects/aop';
interface LoggingOptions {
format: 'JSON' | 'TEXT';
}
class LoggingAdvice implements AdviceProvider {
async attach(
originalMethod: Function,
args: unknown[],
options: LoggingOptions,
targetObject: Record<string, (arg: unknown) => Promise<unknown> | unknown>
): Promise<unknown> {
console.log('Before...');
const result = await originalMethod(...args);
console.log('After...');
return result;
}
}
Define a decorator and attach it to your methods
import { SetMetadata } from '@nestjs/common';
const LOGGING_KEY = 'LOGGING';
export const Logging = (options: LoggingOptions) =>
SetMetadata(LOGGING_KEY, options);
@Injectable()
export class AppService {
@Logging()
getHello(): string {
console.log('Initial method called');
return 'Hello World!';
}
}
Register both as your own aspect
import { AopModule, AspectsRegistry } from '@nestjs-architects/aop';
import { Module } from '@nestjs/common';
@Module({
imports: [AopModule],
providers: [LoggingAdvice],
})
export class LoggingModule {
constructor(
private readonly registry: AspectsRegistry,
private readonly loggingAdvice: LoggingAdvice
) {
this.registry.addAspect(LOGGING_KEY, this.loggingAdvice);
}
}
Now, every time the decoreted method is called the additional code provided by you is executed too.
There are cases when you would like to create a new service dynamically, not just inject it.
For such services you can use AspectsApplier
.
import { AspectsApplier } from '@nestjs-architects/aop';
import { Injectable } from '@nestjs/common';
export class Speaker {
@Logging()
speak() {
console.log('I am not a singleton');
}
}
@Injectable()
export class AppService {
constructor(private readonly aspectsApplier: AspectsApplier) {}
speak() {
const service = new Speaker();
this.aspectsApplier.applyToProvider(service);
service.speak();
}
}