PhoenixETag - conditional request support for phoenix

PhoenixETag

Hex: https://hex.pm/packages/phoenix_etag
GitHub: https://github.com/michalmuskala/phoenix_etag

Conditional request (ETag & modified-since) support for Phoenix.

Usage

The library provides a replacement function for Phoenix.Controller.render/1-4
called PhoenixETag.render_if_stale/1-4 accepting exactly the same arguments.
When called the function expects the view to implement an additional callback:
stale_checks/2 similar to render/2 that is responsible for returning the
etag value and/or last modified value for the current resource.

Additional helpers PhoenixETag.schema_etag/1 and PhoenixETag.schema_last_modified/1 are provided for easily generating the values from a single or multiple schema structs.

# controller
import PhoenixETag

def show(conn, %{"id" => id}) do
  data = MyApp.load_data(id)
  render_if_stale(conn, :show, data: data)
end

# view
import PhoenixETag

def stale_checks("show." <> _format, %{data: data}) do
  [etag: schema_etag(data), last_modified: schema_last_modified(data)]
end

Both the etag and last_modified values are optional. The first one will add an
etag header to the response and perform a stale check against the
if-none-match header. The second one will add a last-modified header to the
response and perform a stale check against the if-modified-since header.
If the headers indicate cache is fresh a 304 Not Modified response is triggered,
and rendering of the response is aborted. If headers indicate cache is stale,
render proceeds as normal, except the extra headers are added to the response.

This allows both saving rendering time on the server and skipping downloading data, which can be significant optimisations for large responses.

The library took some ideas from hex_web and borrowed http dates parsing and formatting from cowlib v2.0.

11 Likes

Hey thanks for extracting/implementing this Michal! Looks very promising and great to have it around in the eco system :slight_smile:

1 Like

Might be a “silly” question but… where do you get :phoenix_etag_date from?