Scrivener Headers Cheatsheet

Quick reference for Scrivener Headers functions and configuration.

Setup

# mix.exs
{:scrivener_ecto, "~> 2.0"},
{:scrivener_headers, "~> 3.2"}

# Repo
defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres
  use Scrivener, page_size: 10
end

Basic Usage

def index(conn, params) do
  page = MyApp.Repo.paginate(MyApp.Person, params)

  conn
  |> Scrivener.Headers.paginate(page)
  |> render("index.json", people: page.entries)
end

Function Signature

Scrivener.Headers.paginate(conn, page, opts \\ [])
ArgumentTypeDescription
connPlug.Conn.tThe connection
pageScrivener.Page.tPaginated result
optskeywordOptional configuration

Returns: Plug.Conn.t with pagination headers set.

Response Headers

HeaderDefault NameValue
Link"link"RFC 5988 pagination URLs
Total"total"page.total_entries
Per-Page"per-page"page.page_size
Total-Pages"total-pages"page.total_pages
Page-Number"page-number"page.page_number
<http://localhost:4000/people?page=1>; rel="first",
<http://localhost:4000/people?page=30>; rel="last",
<http://localhost:4000/people?page=6>; rel="next",
<http://localhost:4000/people?page=4>; rel="prev"
  • first and last are always present
  • prev is present when 1 < page_number <= total_pages
  • next is present when 1 <= page_number < total_pages

Options

Custom Header Names

Scrivener.Headers.paginate(page,
  header_keys: [
    total: "x-total",
    link: "x-link",
    per_page: "x-per-page",
    total_pages: "x-total-pages",
    page_number: "x-page-number"
  ]
)

Reverse Proxy Support

Scrivener.Headers.paginate(page, use_x_forwarded: true)

Reads from these request headers to build URLs:

Request HeaderUsed For
x-forwarded-protoURL scheme
x-forwarded-hostHostname
x-forwarded-portPort number

Scrivener.Page Fields

FieldTypeDescription
page_numberintegerCurrent page (1-indexed)
page_sizeintegerItems per page
total_entriesintegerTotal record count
total_pagesintegerTotal number of pages
entrieslistRecords for this page

Common Patterns

With Ecto Query

page =
  MyApp.Person
  |> where([p], p.age > 30)
  |> order_by([p], desc: p.age)
  |> preload(:friends)
  |> MyApp.Repo.paginate(params)

conn
|> Scrivener.Headers.paginate(page)
|> render("index.json", people: page.entries)

With Custom Page Size

page = MyApp.Repo.paginate(query, %{"page" => "2", "page_size" => "25"})

Behind nginx

# Ensure nginx sets:
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Host $host;
# proxy_set_header X-Forwarded-Port $server_port;

conn
|> Scrivener.Headers.paginate(page, use_x_forwarded: true)
|> render("index.json", data: page.entries)