Recently, I have been building an API as part of my day job. Rails is a great framework to build APIs in, and it has been a joy so far. When building the responses of the API, it’s paramount to understand what HTTP statuses you should utilize, which will in return help the consumers by providing more meaningful responses.
Sure, you could always have a
status property in the response JSON, which will
be a human-readable status code. But, I would like you to think about HTTP status
codes as a nice card on a present, with your beautiful handwriting and best
wishes to whom the gift goes to. HTTP status code don’t just add more semantical
correctness, but they also speak about the nature of the response.
Rails and statuses
Ruby on Rails, being a great framework, adds a very nice layer of abstraction of the status codes, and allows the developers to easily use custom status codes in their responses. I am sure, at some point you have seen something like:
While this might be sometimes redundant, the
render method in Rails allows you
to specify HTTP status codes for the response. But also, being very developer
friendly, Rails allows you to do the same with “human readable” status codes:
The two code samples are identical, they specify the HTTP 201 status code to the response. But, what are the available Rails status codes (as symbols) that you can use? And how does Rails actually do this?
The HTTP status codes list is a quite stable and static list. Although sometimes status codes are added to the list, once you learn it, keeping up to date is rather easy. The latest addition to this list is HTTP 451 - Unavailable For Legal Reasons. You can see the RFC where it was proposed here.
So, how does Rails knows how to create all of these symbols, so we can use them
render methods? Well, what’s very interesting is that Rails actually
doesn’t do much about this. It relies on Rack, which does all of this magic. In
irb session, type:
As you can see, the
Rack::Utils::HTTP_STATUS_CODES is a Hash that has all of
the HTTP status codes, with the corresponding messages. If you open the
you can see how these are programmatically created, including the code that
does the conversion. The status codes are pulled from
this CSV file
and are parsed using this piece of code:
Great. But, how do we get the symbolized versions of the status messages? Well,
Rack does that for us as well. In an
irb session, try this:
As you can see, the
Rack::Utils::SYMBOL_TO_STATUS_CODE constant contains all
of the status codes as symbols. The
also shows the actual conversion, from
Having all of this documentation in place, is rather easy to see how everything falls into place. Rails being a Rack app, can utilize everything that Rack provides, and this is just an example. But, how does Rails actually plugs this into its runtime and has the ability to understand what is the status code?
A bit deeper
Rails’ source code, although very well documented and well structured, can still be overwhelming to someone that hasn’t spent enough time digging through. If you are a Ruby developer, I encourage you to spend some time around Rails’ code - you will learn a lot, I promise you!
So, how does Rails know what status code to apply to the response, if you provide just the symbolized version on the status? Well, let’s start with Rails’ class hierarchy, which is quite nice.
On the surface, or what we usually see as developers, the rendering is done in
the controllers. If you open any of your applications’
you will see something like:
This means that all of our controllers are subclasses of the
ActionController::Base class. If you open the
ActionController::Base class, you will see some very interesting things,
that can be quite informative. But for our use case, we are interested in the
This piece of code, actually includes all of these modules in the
ActionController::Base class, which in return gives us all of the
functionlities that our controllers have. Just look at the module names, like
Flash and so on.
Very self-explanatory, right?
Well, the classes that we are interested in are
ActionController::Rendering. The first one is an abstract class, kind of
like an interface, and the second one is the default implementation class, which
contains all of the rendering mechanisms that Rails uses to render your response.
When we call
render in our controllers, we are executing the
ActionController::Rendering#render method, which does couple of things for us:
it normalizes the arguments, the options and then builds the body of the response.
When it normalizes the arguments, deep inside the
_normalize_render method, it
contains these lines of code:
This piece of code, takes the
:status option from our render call, and
translates the symbol to an actual HTTP code. If we go back to
documentation, we can see the actual implementation of the
Here’s how your
status: :created gets translated to a
status: 201. As you
can see, although Rails’ source is sometimes hard to digest, it’s very well
done and navigating through it is not that hard.
What about exceptions?
So now we know how Rails does the expected rendering, which was created by us, developers. But, what happens when Rails hits an exception of some sort, but it still has to recover from it and return a proper HTTP status code to the client?
Although at first you might expect some sort of a
rescue block in
ActionController::Metal class, this is not the case. Open any Rails app you
have on your computer, and run in it’s root directory:
Actually, the exceptions are handled in Rails’ middleware, more specifically in
ActionDispatch::ShowExceptions middleware class. It wraps a group of
exceptions and maps them to a specific status code. This is the code that does
All of the exceptions that can occur in the request/response lifecycle are mapped
to a specific status code, which will be returned by the middleware if an
exception is rescued. You can see this functionality in
ActionDispatch::ShowExceptions#call method, which invokes the
render_exception method if an
Exception is rescued:
Now, when the exception is being rendered, the
ExceptionWrapper comes into the
picture. It will wrap the exception, and return an appropriate status code, which
is translated from the symbolized variant, to the number version, using:
status_code method invokes the
status_code_for_exception method, which
converts the status code:
And that’s about it. After that, the middleware stack is being executed in order to return the new response, with the exception and the proper HTTP status code attached.
Although all of this Rails “magic” might be a bit much to digest at first, Rails’ source code does quite a good job of explaining what is going on. And as you can see, there’s always something more than meets the eye. If you need to remember anything from this blogpost is that Rails does a very good (and interesting) job at handling HTTP status codes, to make your app’s responses semantically correct and client friendly.
If you got to this point - thanks so much for reading. I know it was a bit of a long journey, and I hope it was informative for you. Looking forward to reading your comments!