Nest: Get response's HTTP status code in Interceptor

Created on 5 Dec 2018  路  5Comments  路  Source: nestjs/nest

Is there any way to get the response's HTTP status code in an interceptor, so that I could create prometheus metrics by HTTP status code?

@Injectable()
export class GlobalMetricsInterceptor implements NestInterceptor {
  constructor(private metricsService: MetricsService) {}

  public intercept(context: ExecutionContext, call: Observable<unknown>): Observable<unknown> {
    const now: number = Date.now();
    const requestedUrl: string = context.getArgByIndex<IncomingMessage>(0).url;
    if (requestedUrl.startsWith('/v5/metrics')) {
      return call.pipe();
    }

    this.metricsService.incrementHttpRequest();

    return call.pipe(
      finalize(() => {
        console.log(`Done in ${Date.now() - now}ms`);
      })
    );
  }
}
core question 馃檶

Most helpful comment

Dear all,

i recently stumbled upon the same problem, that i want to modify the HTTP Status Code in an interceptor. Consider the following example:

I created an ETagInterceptor that calculates the Etag of the response to be send back to my client. If the client makes another request to my API, he may send his etag via if-none-match http header to the API. The API then processes the request and creates the response. If the new etag matches the if-none-match header, the content shall be removed (i.e,. set to '') and the server must return with http code 304 (NOT MODIFIED).

I thought it would be a good idea to apply a Global Interceptor for this scenario, like so (shortened version):

@Injectable()
export class EtagInterceptor implements NestInterceptor {

  intercept(context: ExecutionContext, call$: Observable<any>): Observable<any> {

    // get the request and log it to the console
    const ctx = context.switchToHttp();
    const request = ctx.getRequest();
    const response = ctx.getResponse();

    // the etag a client sends to the API
    const requestETag = request.header('if-none-match');

    return call$.pipe(
      map((content) => {
        // calculate the etag of the current answer
        const responseETag = crypto.createHash('md5').update(JSON.stringify(content)).digest('hex');
        // and append it to the response
        response.setHeader('etag', responseETag);

        // both etags (request and calculated response) are identical
        // set the appropriate Http Status 304 (NOT MODIFIED)
        // and remove the content;
        if (requestETag === responseETag) {
          response.status(304); // <-- this does not work!
          content = '';
        }

        return content;
      }),
    );
  }
}

So for my use-case, how is it not possible to set the status code?
I read the answer from above:

  • 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).

Unfortunately, both points make no sense to me:
1) I can set the status to 304 (NOT MODIFIED) in an interceptor. If, however, at any later stage of the execution, something fails, the status code may be overwritten by an Exception / Error
2) Wouldn't it be better to set the default value for the Response first and then let everyone overwrite it as the request is processed?

All the best and thank you so much for this awesome framework!

All 5 comments

I believe this is a question better fitted for StackOverflow, but you should be able to do

context.switchToHttp().getResponse().statusCode

I've experienced statusCode always being 200 in the interceptor. The following is not an elegant solution, but pushing the getResponse() to the end of the call stack using setTimeout has solved this issue for me:

  return call.pipe(tap(() => {
    setTimeout(() => {
      console.log(context.switchToHttp().getResponse().statusCode)
    }, 0);
  }));

It seems like the interceptor is invoked before the final response code is set on the reponse object.

Actually, it's impossible for a few reasons:

  • 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).

What I would suggest instead:

  • you can catch an error in the interceptor, read a status code, and then rethrow the same error again,
  • when no error was thrown: read HTTP method (GET/POST) and depending on that, determine eventual status code (in Nest, by default, it's respectively 200/201),
  • and the last thing would be to reflect @HttpCode() metadata to ensure that nobody has overloaded a default status code.

Hmm I think using the request and response objects from express or fastify and attach once listeners might be the more elegant solution then. I am worried it's not as generic and and elegant as hoped, but I hope you will ultimately come up with the best solution in the request prometheus metrics module.

Can you share how you currently enrich your services with prometheus metrics?

Dear all,

i recently stumbled upon the same problem, that i want to modify the HTTP Status Code in an interceptor. Consider the following example:

I created an ETagInterceptor that calculates the Etag of the response to be send back to my client. If the client makes another request to my API, he may send his etag via if-none-match http header to the API. The API then processes the request and creates the response. If the new etag matches the if-none-match header, the content shall be removed (i.e,. set to '') and the server must return with http code 304 (NOT MODIFIED).

I thought it would be a good idea to apply a Global Interceptor for this scenario, like so (shortened version):

@Injectable()
export class EtagInterceptor implements NestInterceptor {

  intercept(context: ExecutionContext, call$: Observable<any>): Observable<any> {

    // get the request and log it to the console
    const ctx = context.switchToHttp();
    const request = ctx.getRequest();
    const response = ctx.getResponse();

    // the etag a client sends to the API
    const requestETag = request.header('if-none-match');

    return call$.pipe(
      map((content) => {
        // calculate the etag of the current answer
        const responseETag = crypto.createHash('md5').update(JSON.stringify(content)).digest('hex');
        // and append it to the response
        response.setHeader('etag', responseETag);

        // both etags (request and calculated response) are identical
        // set the appropriate Http Status 304 (NOT MODIFIED)
        // and remove the content;
        if (requestETag === responseETag) {
          response.status(304); // <-- this does not work!
          content = '';
        }

        return content;
      }),
    );
  }
}

So for my use-case, how is it not possible to set the status code?
I read the answer from above:

  • 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).

Unfortunately, both points make no sense to me:
1) I can set the status to 304 (NOT MODIFIED) in an interceptor. If, however, at any later stage of the execution, something fails, the status code may be overwritten by an Exception / Error
2) Wouldn't it be better to set the default value for the Response first and then let everyone overwrite it as the request is processed?

All the best and thank you so much for this awesome framework!

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

menme95 picture menme95  路  3Comments

tronginc picture tronginc  路  3Comments

janckerchen picture janckerchen  路  3Comments

KamGor picture KamGor  路  3Comments

mishelashala picture mishelashala  路  3Comments