graphql-apollo-errors

2.0.3 • Public • Published

graphql-apollo-errors

A small library to handle graphql and apollo errors in a better way

This library is fully tested with 100% coverage

CircleCI Coverage Status Donate

Initiative

Error handling requires few core features to be useful:

  • Ability to customize error in well defined structure cross app
  • Ability to hook the error bubbling (In order to log or store the errors somewhere)
  • Ability to send the error to the client while sending all the relevant information yet keeping all the sensitive information only on the server

Looking around I found only 2 libraries dealing with errors in graphql and apollo - graphql-errors , apollo-errors.

Both libraries are great start, but they are not powerful enough for my opinion, therefore I decided to write my own error handler. Talking with some friends, I understand I'm not alone with this need, so I created this library as open source.

Usage

(Look in the spec files to understand more)

Configure apollo error formatting

import express from 'express';
import bodyParser from 'body-parser';
import { formatErrorGenerator } from 'graphql-apollo-errors';
import schema from './schema';
// You can use what ever you want, this is just an example
var logger = require('minilog')('errors-logger');
 
const formatErrorOptions = {
  logger,
  publicDataPath: 'public', // Only data under this path in the data object will be sent to the client (path parts should be separated by . - some.public.path)
  showLocations: true, // whether to add the graphql locations to the final error (default false)
  showPath: true, // whether to add the graphql path to the final error (default false)
  hideSensitiveData: false, // whether to remove the data object from internal server errors (default true)
  hooks: {
    // This run on the error you really throw from your code (not the graphql error - it means not with path and locations)
    onOriginalError: (originalError) => {logger.info(originalError.message)},
    // This will run on the processed error, which means after we convert it to boom error if needed
    // and after we added the path and location (if requested)
    // If the error is not a boom error, this error won't include the original message but general internal server error message
    // This will run before we take only the payload and the public path of data
    onProcessedError: (processedError) => {logger.info(processedError.message)},
    // This will run on the final error, it will only contains the output.payload, and if you configured the publicDataPath
    // it will only contain this data under the data object
    // If the error is internal error this error will be a wrapped internal error which not contains the sensitive details
    // This is the error which will be sent to the client
    onFinalError: (finalError) => {logger.info(finalError.message)},
  },
  nonBoomTransformer: (nonBoomError) => {error instanceof GraphQLError ? SevenBoom.badRequest(error.message) : SevenBoom.badImplementation(error)}
  // Optional function to transform non-Boom errors, such as those from Apollo & other 3rd-party libraries, into Boom errors
};
const formatError = formatErrorGenerator(formatErrorOptions);
const app = express();
 
app.use('/graphql',
  bodyParser.json(),
  graphqlExpress({
    formatError,
    schema
  })
);
 
app.listen(8080)

Init SevenBoom object The defalut args for SevenBoom are

const defaultArgsDef = [
  {
    name : 'errorCode',
    order: 1
  }, {
    name : 'timeThrown',
    order: 2,
    default: null
  }, {
    name : 'guid',
    order: 3,
    default: null
  }
];

If you want you can change it using the initSevenBoom function:

import { initSevenBoom } from 'graphql-apollo-errors';
const customArgsDefs = [
  {
    name : 'errorCode',
    order: 1
  }
];
initSevenBoom(customArgsDefs);

Use SevenBoom to create your custom error and throw it.

import { SevenBoom } from 'graphql-apollo-errors';
 
// A resolver which throws error
const getUserByIdResolver = (root, { userId }, context) => {
  UserService.getUserById(userId)
  .then((user) => {
    if (user) return user;
    const errorMessage = `User with id: ${userId} not found`;
    const errorData = { userId };
    const errorName = 'USER_NOT_FOUND';
    const err = SevenBoom.notFound(errorMessage, errorData, errorName);
    throw(err);
  }
}

Enjoy your shiny error on the client

{
  "data": {},
  "errors": [
    {
      statusCode: '404',
      error: 'Not Found',
      message: 'User with id: 123 not found.',
      code: 'USER_NOT_FOUND',
      timeThrown: "2017-01-16T21:25:58.536Z",
      guid: 'b6c44655-0aae-486a-8d28-533db6c6c343',
      data: {
        userId: '123'
      }
    }
  ]
}

Upgrade from v1..

There is a lot of changes from v1. (In the implementation, which leads to API changes)

  • onStoredError hook is no longer exist (actually the onOriginalError result is the same as the onStoredError before)
  • You should not use the throwError any more (it was deleted), you can use the native throw now.

How does it work

In general this library contain 2 parts:

  1. SevenBoom - A small library i wrote to create customize errors
  2. format error function - which knows to fetch the real error, hide sensitive server data, add some hooks points and configuration, and pass it to the client.

License

MIT - Do what ever you want

Contribute

I'm open to hear any feedback - new ideas, bugs, needs. Feel free to open issues / PR

Support on PayPal

Hey dude! Help me out for a couple of 🍻!

Donate

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 2.0.3
    2,056
    • latest

Version History

Package Sidebar

Install

npm i graphql-apollo-errors

Weekly Downloads

1,665

Version

2.0.3

License

MIT

Last publish

Collaborators

  • shohamgilad