0

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?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
MisterQvD
  • 27
  • 6
  • You're trying to change the incoming status of a response in `toResponse` which has had it's headers already written (https://stackoverflow.com/questions/7042340/error-cant-set-headers-after-they-are-sent-to-the-client). In general it's also bad practice to reuse and manipulate objects you don't own in that fashion: there may be unintended side-effects to either the owner and you can't ensure who else may have a reference to the underlying data. If you need to pass on a response object where you can change the code create a new one. – possum Jun 30 '23 at 10:18

0 Answers0