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?