I'm using a global interceptor to get response like:
{
"data": "",
"statusCode": int
"message": "string"
}
so I created the interceptor file
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
import { map, Observable } from "rxjs";
export interface Response<T> {
data: T;
}
@Injectable()
export class TransformationInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(map(data => ({
data: data,
statusCode: context.switchToHttp().getResponse().statusCode,
message: data.message
})));
}
}
and put it into my main.ts
In my controller I have:
@Patch('/:userId')
@HttpCode(201)
public async updateUser(
@Param('userId') userId: string,
@Body() userUpdate: UpdateUserDto): Promise<any> {
return await this.usersService.update(userId, userUpdate);
}
and the result is:
{
"data": {
"_id": "621d07d9ea0cdc600fae0f02",
"username": "foo",
"name": "stringwwww",
"__v": 0
},
"statusCode": 201
}
If I want to add my custom message, I need to return an object like:
@Patch('/:userId')
@HttpCode(201)
public async updateUser(
@Param('userId') userId: string,
@Body() userUpdate: UpdateUserDto): Promise<any> {
const result = await this.usersService.update(userId, userUpdate);
return { message: 'User updated', result };
}
but in that case I have twice message and the structure is not correct:
{
"data": {
"message": "User updated",
"result": {
"_id": "621d07d9ea0cdc600fae0f02",
"username": "foo",
"name": "stringwwww",
"__v": 0
}
},
"statusCode": 201,
"message": "User updated"
}
How can I set a custom (optional) message?
I can modify my interceptors like:
@Injectable()
export class TransformationInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(map(data => ({
data: data.res,
statusCode: context.switchToHttp().getResponse().statusCode,
message: data.message
})));
}
}
and my controller like:
@Patch('/:userId')
@HttpCode(201)
public async updateUser(
@Param('userId') userId: string,
@Body() userUpdate: UpdateUserDto): Promise<any> {
const result = await this.usersService.update(userId, userUpdate);
return { message: 'User updated', res: result };
}
and I will get the correct form, but I don't want to add
return { message: 'User updated', res: result };
for each controller
One way to achieve this is as below, but you will be bound to a fixed message per controller
Create a response decorator (response.decorator.ts)
import { SetMetadata } from '@nestjs/common'
export const ResponseMessageKey = 'ResponseMessageKey'
export const ResponseMessage = (message: string) => SetMetadata(ResponseMessageKey, message)
Create a constants file for your responses (response.constants.ts)
export const USER_INSERTED = 'User Inserted'
export const USER_UPDATED = 'User Updated'
export const USER_DELETED = 'User Deleted'
Add the decorator to your controller to set response message metadata
@Patch('/:userId')
@HttpCode(201)
@ResponseMessage(USER_UPDATED)
public async updateUser(
@Param('userId') userId: string,
@Body() userUpdate: UpdateUserDto
): Promise<any> {
const result = await this.usersService.update(userId, userUpdate);
return result;
}
Update your interceptor to read the response message from the metadata set on the controller and add it in the response
import { Reflector } from '@nestjs/core'
@Injectable()
export class TransformationInterceptor<T>
implements NestInterceptor<T, Response<T>>
{
constructor(private reflector: Reflector) {}
intercept(
context: ExecutionContext,
next: CallHandler
): Observable<Response<T>> {
const responseMessage = this.reflector.get<string>(
ResponseMessageKey,
context.getHandler()
) ?? ''
return next.handle().pipe(
map((data) => ({
data,
statusCode: context.switchToHttp().getResponse().statusCode,
message: responseMessage
}))
)
}
}
You could extend this approach to set a list of strings/objects as possible responses (metadata), and based on response code in interceptor, send a particular message as response.message