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

Error "extensions" handling doesn't match spec #925

Closed
dmarkow opened this issue May 19, 2020 · 7 comments · Fixed by #1215
Closed

Error "extensions" handling doesn't match spec #925

dmarkow opened this issue May 19, 2020 · 7 comments · Fixed by #1215

Comments

@dmarkow
Copy link
Contributor

dmarkow commented May 19, 2020

The GraphQL Spec provides information on how errors should be structured, which includes the message, locations, and path keys. The spec then states that anything additional should be nested under an extensions key rather than added at the top level, the reasoning being that future revisions to the spec may add additional keys that could conflict with any custom data.

Right now errors seem to be processed in this way:

  • Resolve with {:error, message: "bad request", something: "something"}
  • During resolution, the error is converted into a %Phase.Error{} struct with all of the keys other than message being pushed into a nested extra field: {:error, message: "bad request", extra: %{something: "something"}}
  • Absinthe.Phase.Document.Result.format_error/1 later merges the extra key back into the top level.
  • The error passed back to the client then looks something like %{errors: [%{message: "bad request", something: "something"}].

In the mean time I can get around this by having my resolvers explicitly nest my extra info under an extensions key.

I'm not sure if this is something that should be changed in Absinthe to match the spec, or if we just need to update the Errors documentation to state that extra info should be in an extensions key.

Environment

  • Elixir version (elixir -v): 1.10.3
  • Absinthe version (mix deps | grep absinthe): 1.5.1
  • Client Framework and version (Relay, Apollo, etc): Apollo Client 2.x

Expected behavior

Based on the spec, I would expect the errors to be returned as:

%{
  errors: [
    %{
      message: "bad request",
      extensions: %{
        something: "something"
      }
    }
  ]
}

Actual behavior

The errors are returned as:

%{
  errors: [
    %{
      message: "bad request",
      something: "something"
    }
  ]
}
@benwilson512
Copy link
Contributor

Hi @dmarkow good catch. This behaviour exists from before extensions were part of the GraphQL spec. I'm fine aiming to change this in 1.6, with the aim to create some kind of warning in a 1.5.x release.

@binaryseed binaryseed changed the title Error handling doesn't match spec Error "extensions" handling doesn't match spec Nov 27, 2020
@mathieuprog
Copy link

Any plans on adding the extensions entry? It seems important to have error codes for the client to use:

switch (err.extensions.code) {
  case 'UNAUTHENTICATED':
    // ...

For now we can just add those custom data in the errors map though, but it's just a pity not to be able to do it the standard way.

@sukinoverse
Copy link

sukinoverse commented Dec 19, 2022

I vote for this issue to have more priority to catch-up with GraphQL spec. 👍🏻

I'm just trying to sell Elixir + Absinthe to my friends, and I've found that Absinthe is outdated with this spec 😂
(most of the flagship's library in other languages already supported)

@maartenvanvliet
Copy link
Contributor

I've pushed a PR to fix this. As this is a breaking change, it is opt-in for now.

@thecynicalpaul
Copy link

@maartenvanvliet how can this be configured when actually using Absinthe? I am looking at pipeline code and seeing that Phase.Document.Result isn't taking in any opts:

def for_document(schema, options \\ []) do
...
  # Format Result
  Phase.Document.Result,
...
end

file ref:

Phase.Document.Result,

Do we need another PR to make this configurable through run opts, etc?

@benwilson512
Copy link
Contributor

benwilson512 commented Jul 7, 2023

@thecynicalpaul you could |> Absinthe.Pipeline.replace(Phase.Document.Result, {Phase.Document.Result, opts}) I believe.

@thecynicalpaul
Copy link

@thecynicalpaul you could |> Absinthe.Pipeline.replace(Phase.Document.Result, {Phase.Document.Result, opts}) I believe.

That helped, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants