Looking for an easy way to raise HTTP errors (e.g. 422) with messages in a RESTful JSON API

TL;DR

I want to be able to do something like raise Plug.HTTPError, plug_status: 501, message: "Some optional message" so that I can easily return helpful errors in a RESTful JSON API that I am working on.

The status code may change, and the message is optional.

I don’t want to have to have to create an exception for this because it comes up in every API I work on, so it makes sense (to me) to have a one-liner solution ready to go.

The Issue, and Why I Am Searching for a Solution

I am working on a RESTful JSON API using Phoenix, and am frequently dealing with the issue of having to abort a request for various reasons, e.g. 422 errors for client-side issues (like some input that is determined to be invalid at some point during the request), 501 errors for server-side issues (like we haven’t implemented an action for a request that contains a certain parameter), and so on.

This can happen at various stages of the request, for a variety of reasons. The conn may or may not be available as part of the function call. But at some point it happens that:

  • The request needs to be aborted with a certain HTTP status code,
  • With a helpful error message, and
  • This should be accomplished in a clean manner with minimal hoop-jumping.

How I’m currently solving the problem.

What I am currently doing is e.g. raise Plug.BadRequestError, plug_status: 501. I’m using Plug.BadRequestError since it is one of the few ready-to-use implementations of a plug exception defined in the Plug library itself.

It works great. Now, in my ErrorJSON module, I just have to add this:

  def render(<<_status::binary-3>> <> ".json", %{reason: %{message: message}} = _assigns) do
    %{errors: %{detail: message}}
  end

And now, I can, in 1 line, from anywhere in the Phoenix application, raise an exception with any desired status code, and terminate the request quickly, easily, and cleanly, while showing a helpful message to the consumer of the API.

The Problem With My Solution

Obviously, the Plug.BadRequestError is intended for a specific use case. Is there a better way to raise a generic exception? (I would strongly prefer not to have to litter each app with a new module for such a common (for me) task.)

Tell Me What You Think

Please let me know your thoughts on the situation.

I have thought that it may be nice to have a generic exception ready to use in the Plug library for this (e.g. Plug.HTTPError, or Plug.Error or Plug.CustomError).

If you have a better solution to this problem, I would be glad to hear it. :slight_smile:

It’s really not - it’s only two (non-blank/doc/end) lines!

A “generic” version would… be an exception with a message and an plug_status field. :man_shrugging:

Modules (especially nearly-empty ones like defexception) are practically free; I don’t see what the problem is here. If your application’s logic has more-specific exceptions, all they need is an plug_status field to work with the fallback implementation of Plug.Exception.

OK. Where should I define this exception?

EDIT: I’m afk but can I just raise a generic Exception for this? That would circumvent the inevitable bike-shedding…