I am trying to create an API interceptor just to learn a bit about them. I wrote a simple app but I get the following error:
Cannot set headers after they are sent to the client.
All the API response interceptor should do is check if the data is of type APIResponse
. If so, then call toResponse()
, which adds a success field based on status. I did this purely for practise reasons. Although I get the response is expect, I also get the error is mentioned earlier. I do not know why and where the code is trying to set the headers a second time.
This is the controller class:
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
async getData() {
console.log('getData');
const data = this.appService.getData();
return APIResponse.success('Welcome to weight-journal-api!', data);
}
}
This the service class:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getData() {
return 'test';
}
}
I made a class with static methods to return APIresponses
to stay consistent with what I return:
import { Injectable } from '@nestjs/common';
import { Response } from 'express';
@Injectable()
export class APIResponse {
constructor(
public status: number,
public message: string,
public data: any
) {}
//the api.response.interceptor.ts will use this method to send the response back to the client
toResponse(res: Response): void {
res.status(this.status).json({
success: this.isSuccess(),
message: this.message,
data: this.data,
});
}
private isSuccess(): boolean {
return this.status >= 200 && this.status < 400;
}
//Controllers will use these methods to return responses
static success(message: string, data: any): APIResponse {
return new APIResponse(200, message, data);
}
static created(message: string, data: any): APIResponse {
return new APIResponse(201, message, data);
}
static error(message: string, data: any): APIResponse {
return new APIResponse(400, message, data);
}
static notFound(message: string, data: any): APIResponse {
return new APIResponse(404, message, data);
}
static conflict(message: string, data: any): APIResponse {
return new APIResponse(409, message, data);
}
}
Here is the API interceptor itself:
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Response } from 'express';
import { APIResponse } from './api.response';
// This interceptor is used to intercept the response of the API and send it back to the client
@Injectable()
export class APIResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const response = context.switchToHttp().getResponse<Response>();
return next.handle().pipe(
tap((data) => {
if (data instanceof APIResponse) {
data.toResponse(response);
}
})
);
}
}
here is the app.module
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { APIResponseInterceptor } from './shared/api.response.interceptor';
@Module({
imports: [],
controllers: [AppController],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: APIResponseInterceptor,
},
AppService,
],
})
export class AppModule {}
Anyone that knows why I get this error?