This is my decorator
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
export const CacheData = createParamDecorator(
(data: any, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
console.log(request.url, request.method, 'request');
const response = ctx.switchToHttp().getResponse();
const reqBody = request.body;
console.log(reqBody, 'reqBody');
console.log(response.raw.req.data, 'response');
console.log(response.raw.req.body, 'response');
console.log(response.raw.data, 'response');
console.log(response.cacheData, 'response');
}
);
in my controller function I'm using the decorator as follows:
getStringsArr(
@Headers('Authorization') auth: string,
@Headers('Country') country = 'DK',
@CacheData() cacheData,
): Array<string> {
return ['Hello', 'World', '!'];
}
so how can I access response data in my CacheData decorator?
Your CacheData
decorator is a param decorator, which means, as far as I know, that it will be executed only when the method handler is called. You have a few options. Here are two.
A method decorator would give you access to the returned data from your function but comes with drawbacks: you don't have access to the execution context, unlike the param decorator and injecting customer services is less elegant. I like this option because it is easy to supply parameters to the decorator.
Since your example is around caching, I suspect you'll want to inject your service there, so option B is probably more fitted to your requirements, but here's an implementation with a method decorator :
const MyMethodDecorator = (params) => {
return (
target: Record<string, unknown>,
_propertyKey: string,
descriptor: PropertyDescriptor,
) => {
const originalMethod = descriptor.value;
descriptor.value = async function (...args) {
const data = await originalMethod.apply(this, args);
// data = ['Hello', 'World', '!']
};
return descriptor;
};
};
@MyMethodDecorator({ ttl: 120, cacheKey: 'stringsArr' })
getStringsArr(
@Headers('Authorization') auth: string,
@Headers('Country') country = 'DK'
): Array<string> {
return ['Hello', 'World', '!'];
}
Interceptors make dependency injection easy since it's like any other NestJS service. I recommend reading and understanding the request lifecycle is you choose this option.
One drawback vs a decorator is that supplying parameters is less elegant but is doable using reflection and a method decorator:
import { applyDecorators, SetMetadata, UseInterceptors } from '@nestjs/common';
@Injectable()
export class MyInterceptor implements NestInterceptor {
constructor(private readonly reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const params = this.reflector.get('MyInterceptorMetadataKey', context.getHandler());
// params = { ttl: 120, cacheKey: 'stringsArr' }
return next.handle().pipe(
tap((response) => {
// data = ['Hello', 'World', '!']
}),
);
}
}
const MyMethodDecorator = (params) =>
applyDecorators(SetMetadata('MyInterceptorMetadataKey', params));
@UseInterceptors(MyInterceptor)
@MyMethodDecorator({ ttl: 120, cacheKey: 'stringsArr' })
getStringsArr(
@Headers('Authorization') auth: string,
@Headers('Country') country = 'DK'
): Array<string> {
return ['Hello', 'World', '!'];
}