Why is Phoenix default ErrorJSON using plural errors with a map as the value?

Hello,

By default Phoenix will generate this on a new project:

# def render("500.json", _assigns) do
#   %{errors: %{detail: "Internal Server Error"}}
# end

def render(template, _assigns) do
  %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
end

Not sure if it is because of an english thing (not my native language) but to me it seems that a map would describe a single error ; plus we are giving the detail for one error.

When I’ll have multiple errors, on an Ecto changeset for instance, I’ll generally return something like that:

%{
  error: %{
    message: "Invalid Request",
    detail: %{
      errors: [] # ... changeset errors as a list
    }
  }
}

Or maybe something like this:

%{
  errors: [
    %{
      message: "Invalid Request",
      detail: [] # ... changeset errors as a list
    }
  ]
}

But I don’t understand the default layout, which is one of the first things I change on a new project.

What is the rationale behind this?

Thank you.

1 Like

@chrismccord Maybe you can shed some light on this!

You sort of answered your own question.

Code gen is only there to get someone started. It’ll never be 100% correct for all use cases and it’s almost never the code that ends up in production. It’s not a crime to change the code generators give you.

As a last resort, you can also make your proposed changes to the generator and open a PR to see if it’ll get accepted.

Hello,

Well I’m asking what is the rationale beyond those choices. Of course I can change it.

Code gen is only there to get someone started. It’ll never be 100% correct for all use cases

My problem here is that is has been correct for 0% use cases in my experience. We always changed it. I wonder why the current layout was chosen.

2 Likes

I’d propose a PR changing it to something more useful then. To me this doesn’t look like a very intentional format.

I dug a little deeper and this is my speculation:

This bit is in the MyApp.ErrorJSON module.

When you use mix phx.gen.json, you will get a resource_json.ex which will have the following:

  def error(%{changeset: changeset}) do
    errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)

    %{errors: errors}
  end

Seems to me like lud’s suggested change is supposed to go into a controller’s JSON module.

Notice the matching :errors root key. That probably makes it easier for API clients to handle errors no matter where it’s coming from (an action on a resource or somewhere up the plug chain)

1 Like

Ecto.Changeset.traverse_errors/2 returns a list though. Not a single object.

1 Like

errors: [...] makes it grammatically correct.

If an error comes from somewhere up the plug chain, MyApp.ErrorJSON is used which will return {errors: ...}. If the error is on a create or update action, the resource’s JSON module will also return {errors: ...}.

That would mean that I, as the API consummer, would only have to worry about handling 1 errors root key instead of handling both error and errors.

But you would not know if its a list or an object. Hence the second “correct” example I gave where errors is always a list.