graphql: Adding extra fields to errors

Let’s say I have a graphql schema with a field that looks like this:

fields := graphql.Fields{
        "hello": &graphql.Field{
            Type: graphql.String,
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                return nil, errors.New("Whoops! An error occurred")
            },
        },
    }

The result would be:

{
  "data": {
    "test": {
      "id": null,
    }
  },
  "errors": [
    {
      "message": "Whoops! An error occurred",
      "locations": [
      ]
    }
  ]
}

What I would like to do is to be able to add extra fields to the errors object. For example:

{
  "data": {
    "test": {
      "id": null,
    }
  },
  "errors": [
    {
      "message": "Whoops! An error occurred",
      "locations": [
      ],
      "field": "hello",
      "code": "WHOOPS_ERROR"
    }
  ]
}

It would be really cool if there’s way to be able to add these extra fields into the error message when an error happens.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 18
  • Comments: 25 (11 by maintainers)

Most upvoted comments

I think that the proposition of @bbuck is crystal clear 😃

But, to sum up I do not want to use the term “field”, but rather “entries”. From spec:

future versions of the spec may describe additional entries to errors.

Because if you are an error between two fields, it’s an entry error no a field error.

Interface

type ErrorEntries map[string]interface{}

type Error struct {
        Message string
        Entries ErrorEntries
}

func (e *Error) Error() string {
        return e.Message
}

func (e *Error) WithEntries(entries ErrorEntries) {
        e.Entries = entries
}

func (e *Error) WithEntry(name string, value interface{}) {
        e.Entries[name] = value
}

func NewError(message string) *Error {
        return &Error{
                Message: message,
                Entries: make(ErrorEntries),
        }
}

Resolver

func (params graphql.ResolveParams) (interface{}, error) {
        return nil, graphql.NewError("Register failed").WithEntry("email", "Must be unique")
}

@sogko or @chris-ramon can you give your vision/opinion?

(as usual, a big thank you for the work)

Great idea!

I think logrus is a good place to start with this one.

logrus.WithFields(logrus.Fields{
        "one": "two",
}).Info("Hey, it's a log")

Given the nature of error (it being an interface and al) we could then do something like this:

type ErrorFields map[string]interface{}

type Error struct {
        Message string
        Fields ErrorFields
}

func (e *Error) Error() string {
        return e.Message
}

func (e *Error) WithFields(fields ErrorFields) {
        e.Fields = fields
}

func (e *Error) WithField(name string, value interface{}) {
        e.Fields[name] = value
}

func NewError(message string) *Error {
        return &Error{
                Message: message,
                Fields: make(ErrorFields),
        }
}

Usage from a Resolve:

func (params graphql.ResolveParams) (interface{}, error) {
        return nil, graphql.NewError("this failed").WithFields(graphql.ErrorFields{
                "one": "two",
        })
}

Probably needs some significant cleaning up, or works. I think it’s a simple and clean API.

This is covered by the latest GraphQL spec. Your resolvers can now return errors that implement gqlerrors.ExtendedError to add an “extensions” field with arbitrary contents. For example:

  "errors": [
    {
      "message": "Name for character with ID 1002 could not be fetched.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "hero", "heroFriends", 1, "name" ],
      "extensions": {
        "code": "CAN_NOT_FETCH_BY_ID",
        "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
      }
    }
  ]

This is covered by the latest GraphQL spec. Your resolvers can now return errors that implement gqlerrors.ExtendedError to add an “extensions” field with arbitrary contents. For example:

  "errors": [
    {
      "message": "Name for character with ID 1002 could not be fetched.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "hero", "heroFriends", 1, "name" ],
      "extensions": {
        "code": "CAN_NOT_FETCH_BY_ID",
        "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
      }
    }
  ]

The issue has been resolved, so I’m closing it.

The errors field in the GraphQL response is defined in the official spec as follows:

Every error must contain an entry with the key message with a string description of the error intended for the developer as a guide to understand and correct the error.

If an error can be associated to a particular point in the requested GraphQL document, it should contain an entry with the key locations with a list of locations, where each location is a map with the keys line and column, both positive numbers starting from 1 which describe the beginning of an associated syntax element.

Having extra fields is actually addressed in the spec as well:

GraphQL servers may provide additional entries to error as they choose to produce more helpful or machine‐readable errors, however future versions of the spec may describe additional entries to errors.

Would love to start a discussion if / how we want to achieve this.

Cheers!