arcanemachine
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. ![]()
Most Liked
D4no0
I would personally use the fallback controller for this:
defmodule MyFallbackController do
use Phoenix.Controller
def call(conn, {:error, :unauthorized, message}) do
# Choose here how to render your custom message
conn
|> put_status(:forbidden)
|> put_view(MyErrorView)
|> render(:"403")
end
end
Then you can use the shape of {:error, :unauthorized, message} (or whichever else you like more) to always fallback to this controller that knows how to render the specific error.
Personally, I always end up using the action_fallback instead of throwing error as it is more of a “functional” approach.
sodapopcan
Ah ya, I guess I’m off topic as I’m not talking APIs (I have yet to have to make one in Phoenix… well, I did sort of work on one at my old job but barely touched it… anyway). But ya, generally if everything is controllers halting is better.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









