Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL error handling #1053

Closed
incompletude opened this issue Jul 20, 2020 · 11 comments · Fixed by #1292
Closed

GraphQL error handling #1053

incompletude opened this issue Jul 20, 2020 · 11 comments · Fixed by #1292

Comments

@incompletude
Copy link

Nest version: lastest.

Throwing errors with Nest HttpException does not play well with GraphQL.

throw new UnauthorizedException() will generate the following:

{
  "errors": [
    {
      "message": "Unauthorized",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "login"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "response": {
            "statusCode": 401,
            "message": "Unauthorized"
          },
          "status": 401,
          "message": "Unauthorized",
          "stacktrace": [stack]
        }
      }
    }
  ],
  "data": null
}

While throw new AuthenticationError("AuthenticationError") from import { AuthenticationError } from "apollo-server-express" will generate the proper friendly error:

{
  "errors": [
    {
      "message": "AuthenticationError",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "login"
      ],
      "extensions": {
        "code": "UNAUTHENTICATED",
        "exception": {
          "stacktrace": [stack]
        }
      }
    }
  ],
  "data": null
}

When using the GraphQL the error responses should be in second format.

https://www.apollographql.com/docs/apollo-server/data/errors/

@kamilmysliwiec kamilmysliwiec transferred this issue from nestjs/nest Jul 23, 2020
@JVMartin
Copy link

This is a show-stopper - every single thrown HTTP exception results in INTERNAL_SERVER_ERROR, making GraphQL unusable in nest.

Any updates?

@incompletude
Copy link
Author

incompletude commented Sep 15, 2020

This is a show-stopper - every single thrown HTTP exception results in INTERNAL_SERVER_ERROR, making GraphQL unusable in nest.

Any updates?

I found a workaround.

https://www.apollographql.com/docs/apollo-server/data/errors/

You can import these erros like:

import { UserInputError } from "apollo-server-express"

Then it will work as intended.

@prokopsimek
Copy link

prokopsimek commented Nov 16, 2020

+1

The nestjs-graphql lib could re-export native apollo errors and then we can use formatError to re-throw correct graphql error.

//edit: Ok, it's possible to install apollo-server-errors an use these errors.

@delucca
Copy link

delucca commented Nov 24, 2020

+1

@leograde
Copy link

leograde commented Dec 8, 2020

You can pass a formatError when registering the GraphQL module such as:

      formatError: (error: GraphQLError) => {
        const graphQLFormattedError: GraphQLFormattedError = {
          message: error.extensions?.exception?.response?.message || error.message,
        };
        return graphQLFormattedError;
      }

I found the answer here: https://stackoverflow.com/a/64129469/5959721

@itstheandre
Copy link

So what is the possible fix for this currently? Is that we use apollo server errors instead of the BadRequestException of nestjs?

How do we handle with class validator errors as well? Is there a way to check the difference between both possible error types?

nirga added a commit to nirga/graphql that referenced this issue Dec 27, 2020
nirga added a commit to nirga/graphql that referenced this issue Dec 27, 2020
nirga added a commit to nirga/graphql that referenced this issue Dec 27, 2020
@adamgajzlerowicz
Copy link

@leograde Thanks for the tip.

I added my own custom formatter that looks like this

formatError: (error: GraphQLError) => error.extensions?.exception?.response || || error.message,

Is there any benefit behind returning the GraphQLFormattedError rather than the custom structure?

@jfrader

This comment has been minimized.

@dayota
Copy link

dayota commented Feb 27, 2021

Another patch solution ...

  • catching validation errors with global ValidationPipe and transform to UserInputError from apollo-server-errors
  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      exceptionFactory: (errors: ValidationError[]): => {
        return new UserInputError('VALIDATION_ERROR', {
          invalidArgs: errors,
        });
      },
    }),
  );
  • catching and formatting UserInputError to GraphQLFormattedError
    GraphQLModule.forRoot({
      autoSchemaFile: true,
      debug: false,
      formatError: (error: GraphQLError) => {
        if (error.message === 'VALIDATION_ERROR') {
          const extensions = {
            code: 'VALIDATION_ERROR',
            errors: [],
          };

          Object.keys(error.extensions.invalidArgs).forEach((key) => {
            const constraints = [];
            Object.keys(error.extensions.invalidArgs[key].constraints).forEach(
              (_key) => {
                constraints.push(
                  error.extensions.invalidArgs[key].constraints[_key],
                );
              },
            );

            extensions.errors.push({
              field: error.extensions.invalidArgs[key].property,
              errors: constraints,
            });
          });

          const graphQLFormattedError: GraphQLFormattedError = {
            message: 'VALIDATION_ERROR',
            extensions: extensions,
          };

          return graphQLFormattedError;
        } else {
          return error;
        }
      },
    }),

Thanks to @prokopsimek @leograde

@veritem
Copy link

veritem commented Feb 28, 2021

Another patch solution ...

  • catching validation errors with global ValidationPipe and transform to UserInputError from apollo-server-errors
  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      exceptionFactory: (errors: ValidationError[]): => {
        return new UserInputError('VALIDATION_ERROR', {
          invalidArgs: errors,
        });
      },
    }),
  );
  • catching and formatting UserInputError to GraphQLFormattedError
    GraphQLModule.forRoot({
      autoSchemaFile: true,
      debug: false,
      formatError: (error: GraphQLError) => {
        if (error.message === 'VALIDATION_ERROR') {
          const extensions = {
            code: 'VALIDATION_ERROR',
            errors: [],
          };

          Object.keys(error.extensions.invalidArgs).forEach((key) => {
            const constraints = [];
            Object.keys(error.extensions.invalidArgs[key].constraints).forEach(
              (_key) => {
                constraints.push(
                  error.extensions.invalidArgs[key].constraints[_key],
                );
              },
            );

            extensions.errors.push({
              field: error.extensions.invalidArgs[key].property,
              errors: constraints,
            });
          });

          const graphQLFormattedError: GraphQLFormattedError = {
            message: 'VALIDATION_ERROR',
            extensions: extensions,
          };

          return graphQLFormattedError;
        } else {
          return error;
        }
      },
    }),

Thanks to @prokopsimek @leograde

This worked for me thanks @dayota

@kamilmysliwiec
Copy link
Member

Let's track this here #1292

@nestjs nestjs locked and limited conversation to collaborators Mar 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.