Multiverse
This plug helps to manage multiple API versions based on request and response gateways. This is an awesome practice to hide your backward compatibility. It allows to have your code in a latest possible version, without duplicating controllers or models.
Best practice is to store consumer version upon his first request and add a error_handler
that will load if from a storage, and set it for user automatically. So, basically, they won’t need to know which version they are using, until they will explicitly set it via request header.
Inspired by Stripe API. Read more at MOVE FAST, DON’T BREAK YOUR API.
Installation
The package (take look at hex.pm) can be installed as:
Add
multiverse
to your list of dependencies inmix.exs
:def deps do [{:multiverse, "~> 0.4.2"}] end
Make sure that
multiverse
is available at runtime in your production:def application do [applications: [:multiverse]] end
How to use
Insert this plug into your API pipeline (
router.ex
):pipeline :api do plug :accepts, ["json"] plug :put_secure_browser_headers plug Multiverse end
Create your first API gateway
defmodule GateName do @behaviour MultiverseGate def mutate_request(%Plug.Conn{} = conn) do # Mutate your request here IO.inspect "GateName.mutate_request applied to request" conn end def mutate_response(%Plug.Conn{} = conn) do # Mutate your response here IO.inspect "GateName.mutate_response applied to response" conn end end
Attach gate to multiverse:
pipeline :api do plug :accepts, ["json"] plug :put_secure_browser_headers plug Multiverse, gates: [ "2016-07-31": GateName ] end
Notice: your API versions should be strings in YYYY-MM-DD format to be appropriately compared to current version.
Send your API requests with
X-API-Version
header with version lower than2016-07-31
.
Custom version header
You can use any version headers by passing option to Multiverse:
```elixir
pipeline :api do
plug :accepts, ["json"]
plug :put_secure_browser_headers
plug Multiverse, gates: [
"2016-07-31": GateName
], version_header: "X-My-API-Version"
end
```
Custom error handlers
Sometimes clients are sending corrupted version headers, by default Multiverse will fallback to “latest” version. But you can set your own handler for this situations:
```elixir
pipeline :api do
plug :accepts, ["json"]
plug :put_secure_browser_headers
plug Multiverse, gates: [
"2016-07-31": GateName
], error_callback: &IO.inspect/1
end
```
Custom error callback should be a function that returns string:
```elixir
def custom_error_callback(%Plug.Conn{} = _conn, reason) do
IO.inspect reason
"2015-01-03"
end
```
Structuring your tests
Split your tests into versions:
$ ls -l test/acceptance total 0 drwxr-xr-x 2 andrew staff 68 Aug 1 19:23 GateName drwxr-xr-x 2 andrew staff 68 Aug 1 19:24 OlderGateName
Avoid touching request or response in old tests. Create API gates and matching folder in acceptance tests.