7

I'm using an interceptor to transform my response. I want to set the HttpStatus inside but the code I'm using now doesn't work.

import { CallHandler, ExecutionContext, NestInterceptor, SetMetadata } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { classToPlain } from 'class-transformer';
import { ApiResponse } from '../models/apiResponse';

export class TransformInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler<ApiResponse | any>,
  ): Observable<ApiResponse | any> {
    return next.handle().pipe(
      map(data => {
        const http = context.switchToHttp();
        const res = http.getResponse();

        if(data instanceof ApiResponse) {
          if(data.status !== undefined) {
            res.status(data.status);
          }
        }

        return classToPlain(data);
      }),
    );
  }
}

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
toonvanstrijp
  • 417
  • 1
  • 5
  • 18

1 Answers1

10

Updated Answer

Since nest version 6.1.0, it is possible to set the status code in an interceptor; it will not be overwritten anymore (see this PR):

context.switchToHttp()
      .getResponse()
      .status(205);

Outdated Answer

Setting the status code from an Interceptor is impossible (see this issue) because:

  • sometimes response status codes are dependent on exceptions and exception filters are executed after interceptors,
  • global response controller's logic is the last step performed just before sending a final result through the network (that's the place where default status codes come in).

So your status code will be overridden by the default code 200/201 or an exception filter.


As a (hacky) workaround, you can use exception filters to set the status code in interceptors:

1) Create your own exception as a wrapper around HttpException:

export class StatusException extends HttpException {
  constructor(data, status: HttpStatus) {
    super(data, status);
  }
}

2) Create an exception filter that sets the response code and returns the data:

@Catch(StatusException)
export class StatusFilter implements ExceptionFilter {
  catch(exception: StatusException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();
    console.log(`Setting status to ${status}`);
    response.status(status).json(exception.message);
  }
}

3) Instead of setting the response throw the according exception in your interceptor:

@Injectable()
export class StatusInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next): Observable<any> {
    return next.handle().pipe(
      map((data: any) => {
        if (data.text === 'created') {
          throw new StatusException(data, HttpStatus.CREATED);
        } else {
          throw new StatusException(data, HttpStatus.ACCEPTED);
        }
      }),
    );
  }
}

4) Use it in your controller:

@UseFilters(StatusFilter)
@UseInterceptors(StatusInterceptor)
@Controller()
export class AppController {
  @Get(':param')
  async get(@Param('param') param) {
    return { text: param };
  }
}

Alternatively, you can inject @Res() in your controller and directly control the response code (but also losing interceptors, exception filters, etc.)

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • thanks. This will do it for now, but still it’s not a good solution since the middleware/interceptors provided later on won’t run anymore. Think I’ll make a pull request to the framework with some changes that allows the user to set the status code freely. But thanks for those work arounds. – toonvanstrijp Mar 31 '19 at 10:42
  • According to the exception filter documentation, the 'HttpException' is treated by default, so you simple need to throw it with the return status you want on your interceptor and it will work. – Felipe Issa Jul 17 '19 at 16:49
  • I'm the creator of the PR ;) but thanks for your work around! :D – toonvanstrijp Aug 19 '19 at 16:55
  • Haha, sorry, didn't see that. Thanks for the PR! :-) – Kim Kern Aug 19 '19 at 16:57