StellateHQ / fuse

Fuse: The fastest way to build and query great APIs with TypeScript

Home Page:https://fusedata.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

RFC: Error handling

mxstbr opened this issue · comments

Summary

Error handling is a hotly debated topic in the GraphQL ecosystem that comes with no broadly recommended best practice today.

Proposed Solution

The way we handle this at Stellate (which we quite like) looks like this:

import { GraphQLError } from 'graphql'

export abstract class StellateError extends GraphQLError {
  abstract readonly name: string

  constructor(
    message: string,
    extensions: {
      code: string
      http?: {
        status: number
      }
    },
  ) {
    super(message, {
      extensions,
    })
  }
}

/** For use when user is not authenticated or unknown. */
export class AuthenticationError extends StellateError {
  name = 'UnauthenticatedError'
  constructor(message = 'Unauthenticated') {
    super(message, { http: { status: 401 }, code: 'UNAUTHENTICATED' })
  }
}

/** For use when a resource is not found or not accessible by an authenticated user. */
export class ForbiddenError extends StellateError {
  name = 'ForbiddenError'
  constructor(message = 'Forbidden') {
    super(message, { http: { status: 403 }, code: 'FORBIDDEN' })
  }
}

/** For use when a resource is not found. */
export class NotFoundError extends StellateError {
  name = 'NotFoundError'
  constructor(message = 'Not Found') {
    super(message, { http: { status: 404 }, code: 'NOT_FOUND' })
  }
}

/** For use when any input was invalid or when a resource does not exist but is assumed to exist. */
export class BadRequestError extends StellateError {
  name = 'BadRequestError'
  constructor(message = 'Bad Request') {
    super(message, { http: { status: 400 }, code: 'BAD_REQUEST' })
  }
}

/** For use when any internal billing calls encounter invalid states. */
export class PaymentError extends StellateError {
  name = 'PaymentError'
  // TODO: Replace with more specific error per-service
  constructor(message = 'An invalid Payment Request has been made') {
    super(message, { http: { status: 400 }, code: 'BAD_PAYMENT_REQUEST' })
  }
}

/** For use when any internal call or fetch request to another API fails. */
export class SlackCallError extends StellateError {
  name = 'SlackAPIError'
  // TODO: Replace with more specific error per-service
  constructor(message = 'Internal call to Slack failed') {
    super(message, { http: { status: 500 }, code: 'SLACK_API_ERROR' })
  }
}

export class ValidationError extends StellateError {
  name = 'ValidationError'
  constructor(message: string) {
    super(message, { code: 'GRAPHQL_VALIDATION_FAILED' })
  }
}

export class UserInputError extends StellateError {
  name = 'UserInputError'
  constructor(message: string) {
    super(message, { code: 'BAD_USER_INPUT' })
  }
}

Todo: Figure out what it would look like to abstract this & guide people down the path of success of error handling in GraphQL with Fuse.js.

I'm too slow! @JoviDeCroock already added a first version here: e85e777