Plugging PlugRequireHeader
Over the last few weeks I've been working on another Elixir Hex package - PlugRequireHeader. While not at 1.0 quite yet, I still would like to share it with you after taking a detour into the land of Elixir plugs.
Plug, for those that don't know about it, is an HTTP middleware specification similar to Ruby's Rack. It also comes with adapters to HTTP servers such as Cowboy, which is prevalent within the Elixir community. Just like Ruby on Rails is built on top of Rack, so is Phoenix built on top of Plug. In fact, a Phoenix controller is "just another Plug".
As with any stack of components, great flexibility is gained by the possibility of composing pipelines. A plug can partially handle a request before feeding its result into the next plug and so forth. If at any step in the pipeline a plug decides that the request is invalid or shouldn't be processed any further, it's possible to halt the pipeline.
Taking advantage of this, it's possible to eliminate quite a few conditionals in your controller actions. Let's say that you're building an API that requires an API key to be set as a request header. We could have every action inspect the request headers, extract the API key if it's present, verify it, and then get down to real business. Now, if the API key isn't present or if it's invalid we of course need to respond accordingly, perhaps with an HTTP 403 (forbidden) status code and some message.
But what if we're not looking for a header that absolutely must be set in order to continue processing the request? Well, we could handle that by setting a default value of some sort. Clearly, there are quite a few things to do in our controller action before we even get to the part where we process parameters and render some response.
PlugRequireHeader alleviates some of this pain by extracting most of the generics into a plug. The required headers are extracted and assigned to specified keys on the conn.assigns
map. Missing headers are handled, either via configured responses for the typical cases or by specified callbacks for the more advanced scenarios.
For more information, take a look at the usage examples in the README as well as the online documentation. Code contributions and suggestions are, as always, welcome.