home

Action-Specific Plug Pipelines for Phoenix

13 Feb 2020

For our APIs, most of our actions need a common set of plugs (Elixir's middleware). Phoenix has the concept of pipelines, which are a sequence of plugs you can apply to a group of routes. But our actions tend to need action-specific configuraiton.

The way to do this in Phoenix is via the plug macro with a guard clause on your aciton:

plug App.Plugs.Context, [route: "user_list"] when action == :index
plug App.Plugs.Cache, [:public, ttl: 60] when action == :index
plug App.Plugs.Validate, [
  search: [string: [max: 50]],
  page: [:int, default: 1]
] when action == :index

It's a mouthful! But with a bit of code, we can turn the above into:

pipeline :index, [
  context: [route: "user_list"],
  cache: [:public, ttl: 60],
  validate: [
    search: [string: [max: 50]],
    page: [:int, default: 1]
  ]
]

pipeline/2 is just a macro that converts this improved version into the original:

defmacro pipeline(action, plugs) do
  for {plug, config} <- plugs do
    plug = case plug do
      :context -> App.Plugs.Context
      :cache -> App.Plugs.Cache
      :validate -> App.Plugs.Validate
    end

    quote location: :keep do
      plug unquote(plug), unquote(config) when var!(action) == unquote(action)
    end
  end
end

I hope someone finds that useful.