Absinthe Book Typo?

I was following a hint I got from Ben Wilson, and I could not get Middleware working with error handling (almost the whole a day).

In the book, we have:

​def​ call(res, _) ​do​
  ​with​ %{​errors:​ [%Ecto.Changeset{} = changeset]} <- res ​do​
    %{res |
      ​value:​ %{​errors:​ transform_errors(changeset)},
      ​errors:​ [],
    }
  ​end​
​end

suspect it should look something like

​def​ call(res, _) ​do​
  ​with​ %{​errors:​ [%Ecto.Changeset{} = changeset]} <- res ​do​
    %{res |
      ​errors:​ transform_errors(changeset)
    }
  ​end​
​end

At least that works for me… Perhaps I have a different setup from the book.

Odd.

Looks v obvious now.

Perhaps it helps the authors or fellow beta readers.

I haven’t read the book, and don’t know anything about your setup, but maybe

​def​ call(res, _) ​do​
  ​with​ %{​errors:​ [%Ecto.Changeset{} = changeset]} <- res ​do​
    %{res |
      ​value:​ %{​errors:​ transform_errors(changeset)},
      ​errors:​ [],
    }
  ​end​
​end

emphasizes that errors in the changeset are data, and hence are being put in values which then gets shown to the end user (~ 4xx http status codes). And errors in :errors are like exceptions (~ 5xx http status codes) which should be dealt with by the developer.

1 Like

yeh I spent most of the day thinking the same thing!

(and perhaps there is something to it)

but just transforming the ‘input errors’ into ‘output errors’ did the trick!

Middleware Resolution is like Plug, a struct is passed around in a monoidal fashion, so I suppose it makes sense…

Hey @mmport80

It depends on where you are in the book. There are instances where we turn changesets into GraphQL errors, and there are spots later in the same chapter where we explore using “errors as data”. The def call you show from the book like it’s from the “errors as data” bit.

1 Like

It is not a typo. My guess is that you are in page 128 in the current beta. If you followed the book, your mutation by now should return an object

  object :menu_item_result do
    field :menu_item, :menu_item
    field :errors, list_of(:input_error)
  end

The errors field above is the value:​ %{​errors:​ transform_errors(changeset)},

The resolution struct itself has two keys: :value and :errors. The confusing part is that the key :errors is the same name as the field but other than that they serve a different purpose. Had the :errors key of the resolution struct been left with its content, the errors would buble up and you would had a return like

{
  "errors": [
    {
      "message": " Some error message '}'",
      "locations": [
        {
          "line": 3,
          "column": 0
        }
      ]
    }
  ]
} 

Which in GraphQL is considered something like throwing an exception

So what the code in the call does is:

  • Gets the errors from the :errors key of the resolution struct,
  • Transforms them in key value pairs
  • Assigns them in the :errors key inside the :values key so they can be used in the return
  • Empties the list of the :errors key of the resolution struct so they don’t buble up.
3 Likes

Nailed it - that’s what I deserve for dipping in and out of the book! : )