The spec (http://facebook.github.io/graphql/#sec-Errors) defines errors as a map with a mandatory "message" key, but other keys are fine.
All that is passed as {:error} by a resolver gets converted to a char, as seen by the protocol String.Chars not implemented error when you pass something else.
One should be able to pass both a map or a list of maps, like this:
{:error, %{message: "msg", code: "error_code"}
{:error, [ %{message: "err1", ...}, %{message: ....} ] }
This is a really crucial feature for me for proper error reporting.
Also, I have no idea how to handle changeset errors, because passing either {:ok, changeset} or {:error, changeset} with invalid changesets gives be the same string.chars not implemented error.
It would be trivial to write a little converter if errors as mentioned above would be accepted.
As a workaround I will create some Error struct and implement the String.Chars protocol, but I think this should be supported out of the box.
We have no problem supporting "more complex" errors, provided they don't break spec, and are always open to well-structured PRs (with tests, especially) that we can have discussions around.
I created a PR for that: https://github.com/absinthe-graphql/absinthe/pull/201
Please give any feedback to make it acceptable for merge.
Closed by #201, will be part of the next release.
Reopening this -- as @tlvenn pointed out to me on Slack, the "multiple" errors version of this isn't implemented.
@benwilson512 Do you have any strong (execution-related) reservations about resolutions returning multiple errors? There doesn't appear to be any spec-level restrictions.
Changesets are hard to encode into JSON. I tried to achieve this before, but, errors is considering for developers to debug the application (since it's lack of i18n, it's useless for end users).
Here is my solution:
def response({status, payload}) do
case payload do
%Ecto.Changeset{} = changeset ->
{
status,
message: "Validation failed.",
changeset: %{
errors: changeset
|> Ecto.Changeset.traverse_errors(fn
{msg, opts} -> String.replace(msg, "%{count}", to_string(opts[:count]))
msg -> msg
end),
action: changeset.action
}
}
_ -> {status, payload}
end
end
and in resolvers:
```
def create(%{blog: blog_params}, _) do
%Blog{}
|> Blog.changeset(blog_params)
|> Repo.insert
|> response
end
def update(%{id: id, blog: blog_params}, _) do
case Repo.get(Blog, id) do
nil -> {:error, "Blog with id #{id} not found."}
blog ->
blog
|> Repo.preload(:variants)
|> Blog.changeset(blog_params)
|> Repo.update
|> response
end
```
The custom attributes returning to end-users is supported on master branch of absinthe.
Hope that can help you. 馃嵒
@quangbuule yes, it is (and out in the latest release) -- we're discussing whether or not multiple errors should be allowed to be returned from a field resolver. BTW, if you'd like to discuss Ecto changeset errors in more detail, https://github.com/absinthe-graphql/absinthe_ecto/issues would be a great place to do it. That's where all the Ecto-specific stuff will go.
Multiple errors are being handled in #248.
To send back changeset errors, I assume it would be best to rely on Phoenix's generated web/views/changeset_view.ex?
{:error, Map.merge(%{
message: "Validation failed."
}, App.ChangesetView.render("error.json", %{changeset: changeset}))}
Or change web/views/changeset_view.ex to include the message by default:
def render("error.json", %{changeset: changeset}) do
%{message: "Validation failed.", errors: translate_errors(changeset)}
end
In the resolver:
{:error, App.ChangesetView.render("error.json", %{changeset: changeset})}
@clessg We don't recommend using Phoenix views for formatting changesets, although I suppose it may work -- in the past we've handled this with a function, and something more comprehensive is planned for the absinthe_ecto project. Ideas are probably best put into its Issues: https://github.com/absinthe-graphql/absinthe_ecto/issues -- my guess is that comprehensive Ecto changeset error handling may rely on the resolution middleware work that @benwilson512 is completing.
Multiple errors supported by #248, now merged into master, will be released as part of v1.2.4.
Thanks @bruce !
@quangbuule i'm dealing with changeset encoding issue and your solution works like charm, saved my day, thanks 馃憤
Most helpful comment
Changesets are hard to encode into JSON. I tried to achieve this before, but, errors is considering for developers to debug the application (since it's lack of i18n, it's useless for end users).
Here is my solution:
and in resolvers:
```
def create(%{blog: blog_params}, _) do
%Blog{}
|> Blog.changeset(blog_params)
|> Repo.insert
|> response
end
def update(%{id: id, blog: blog_params}, _) do
case Repo.get(Blog, id) do
nil -> {:error, "Blog with id #{id} not found."}
blog ->
blog
|> Repo.preload(:variants)
|> Blog.changeset(blog_params)
|> Repo.update
|> response
end
```
The custom attributes returning to end-users is supported on master branch of
absinthe.Hope that can help you. 馃嵒