docs.nestjs.com icon indicating copy to clipboard operation
docs.nestjs.com copied to clipboard

docs(exception-filters): pass full HttpAdapterHost

Open pepijnolivier opened this issue 1 year ago • 6 comments

PR Checklist

Please check if your PR fulfills the following requirements:

  • [x] The commit message follows our guidelines: https://github.com/nestjs/docs.nestjs.com/blob/master/CONTRIBUTING.md

PR Type

What kind of change does this PR introduce?

Fix documentation issue: pass in the entire HttpAdapterHost as the example code requires

  • [ ] Bugfix
  • [ ] Feature
  • [ ] Code style update (formatting, local variables)
  • [ ] Refactoring (no functional changes, no api changes)
  • [ ] Build related changes
  • [x] Docs
  • [ ] Other... Please describe:

What is the current behavior?

Typescript error when using the example code

Argument of type 'AbstractHttpAdapter<any, any, any>' is not assignable to parameter of type 'HttpAdapterHost<AbstractHttpAdapter<any, any, any>>'.
  Property 'httpAdapter' is missing in type 'AbstractHttpAdapter<any, any, any>' but required in type 'HttpAdapterHost<AbstractHttpAdapter<any, any, any>>'.

What is the new behavior?

Fix typescript error

Does this PR introduce a breaking change?

  • [ ] Yes
  • [x] No

Other information

None

pepijnolivier avatar Feb 28 '23 13:02 pepijnolivier

If you'll notice, this is actually a different filter than the one mentioned above, and thus has a different signature and parameters. Just above is a full implementation while this one is an extension of the BaseExceptionFilter

jmcdo29 avatar Feb 28 '23 16:02 jmcdo29

As specified by Jay Actually the documentation is correct, let's try to specify it again but with code snippets:

This snippet code

import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';

@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    super.catch(exception, host);
  }
}

and linked to this:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));

  await app.listen(3000);
}
bootstrap();

And not to this code snippet:

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: unknown, host: ArgumentsHost): void {
    // In certain situations `httpAdapter` might not be available in the
    // constructor method, thus we should resolve it here.
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}

However, I think it's already the 6th PR with request for this change (list of closed PRs):

  • https://github.com/nestjs/docs.nestjs.com/pull/2506
  • https://github.com/nestjs/docs.nestjs.com/pull/2260
  • https://github.com/nestjs/docs.nestjs.com/pull/2201
  • https://github.com/nestjs/docs.nestjs.com/pull/2460
  • https://github.com/nestjs/docs.nestjs.com/pull/2437

So it still confuses people a bit about this point in the documentation, maybe because it's skipped while reading? or why the classes have the same name? 🤔 Maybe an alert could fix this or change the name of one of the two classes in the snippets? 🤔

Tony133 avatar Feb 28 '23 22:02 Tony133

maybe because it's skipped while reading?

I guess isn't that clear that we should read it sequentially

... change the name of one of the two classes in the snippets?

this sounds good to me


@pepijnolivier could you please tell us why did you thought that was wrong?

micalevisk avatar Mar 01 '23 03:03 micalevisk

... change the name of one of the two classes in the snippets?

This sounds good to me too

kamilmysliwiec avatar Mar 01 '23 07:03 kamilmysliwiec

@micalevisk @kamilmysliwiec

Thanks for the fast responses! You guys are correct of course.

maybe because it's skipped while reading?

This is definitely it. 🙂 All I wanted to do was to quickly create a "catch-all exception handler" in my project, so I copy-pasted the code and scrolled down and copy-pasted the snippet that adds the handler to the bootstrap function as well.

Now that I see what happened, here is my suggestion on how the docs can be improved:

  • Either use a different className for the 2 examples (full implementation vs extension of the BaseExceptionFilter)
  • Or, the first example with the full implementation could just receive httpAdapter rather than the { httpAdapter } in its constructor. This would make both Exception implementations compatible with each other (and swappable).

☝️I think the second option would be best, this way other readers like me that quickly skimmed over the documentation to "quickly copy the global exception handler" should be able to do that without any issue.

pepijnolivier avatar Mar 01 '23 11:03 pepijnolivier

Either use a different className for the 2 examples (full implementation vs extension of the BaseExceptionFilter)

Let's proceed with this option. We can't really do the other one since, in some specific situations, httpAdapter might not be available in the constructor

kamilmysliwiec avatar Jun 23 '23 09:06 kamilmysliwiec