Shell layer RFC 5988 Link header generation for pagination.
RFC 5988: Web Linking https://datatracker.ietf.org/doc/html/rfc5988
SIDE EFFECTS:
Link headers provide hypermedia controls for pagination navigation. They allow clients to discover next, prev, first, and last pages without parsing response bodies.
Format: Link: </api/v1/users?limit=20&offset=20>; rel="next", </api/v1/users?limit=20&offset=0>; rel="first", </api/v1/users?limit=20&offset=980>; rel="last"
Supported Relations:
Shell layer RFC 5988 Link header generation for pagination.
RFC 5988: Web Linking
https://datatracker.ietf.org/doc/html/rfc5988
SIDE EFFECTS:
- String building and URL encoding
- Logging
Link headers provide hypermedia controls for pagination navigation.
They allow clients to discover next, prev, first, and last pages without
parsing response bodies.
Format:
Link: </api/v1/users?limit=20&offset=20>; rel="next",
</api/v1/users?limit=20&offset=0>; rel="first",
</api/v1/users?limit=20&offset=980>; rel="last"
Supported Relations:
- first: First page of results
- last: Last page of results (offset pagination only)
- prev: Previous page
- next: Next page
- self: Current page(build-cursor-links base-path params pagination-meta)Build link data for cursor-based pagination.
Generates URLs for prev, next, and self relations based on cursor pagination metadata. Note: first and last are not available with cursor pagination (would require scanning entire dataset).
Args: base-path - Base URL path (e.g., "/api/v1/users") params - Current query parameters (map) pagination-meta - Cursor pagination metadata with keys: :limit - Items per page :has-next - Boolean indicating if next page exists :has-prev - Boolean indicating if previous page exists :next-cursor - Cursor for next page (if has-next) :prev-cursor - Cursor for previous page (if has-prev)
Returns: Vector of link data maps [{:url ... :rel ...} ...]
Side Effects:
Example: (build-cursor-links "/api/v1/users" {:limit 20 :cursor "eyJ..." :sort "name"} {:limit 20 :has-next true :has-prev true :next-cursor "eyJpZCI6IjEyMyI..." :prev-cursor "eyJpZCI6IjQ1NiI..."}) ;;=> [{:url "/api/v1/users?limit=20&cursor=eyJpZCI6IjQ1NiI...&sort=name" :rel :prev} ;; {:url "/api/v1/users?limit=20&cursor=eyJ...&sort=name" :rel :self} ;; {:url "/api/v1/users?limit=20&cursor=eyJpZCI6IjEyMyI...&sort=name" :rel :next}]
Build link data for cursor-based pagination.
Generates URLs for prev, next, and self relations based on cursor pagination
metadata. Note: first and last are not available with cursor pagination
(would require scanning entire dataset).
Args:
base-path - Base URL path (e.g., "/api/v1/users")
params - Current query parameters (map)
pagination-meta - Cursor pagination metadata with keys:
:limit - Items per page
:has-next - Boolean indicating if next page exists
:has-prev - Boolean indicating if previous page exists
:next-cursor - Cursor for next page (if has-next)
:prev-cursor - Cursor for previous page (if has-prev)
Returns:
Vector of link data maps [{:url ... :rel ...} ...]
Side Effects:
- URL building
- Logging
Example:
(build-cursor-links
"/api/v1/users"
{:limit 20 :cursor "eyJ..." :sort "name"}
{:limit 20
:has-next true
:has-prev true
:next-cursor "eyJpZCI6IjEyMyI..."
:prev-cursor "eyJpZCI6IjQ1NiI..."})
;;=> [{:url "/api/v1/users?limit=20&cursor=eyJpZCI6IjQ1NiI...&sort=name" :rel :prev}
;; {:url "/api/v1/users?limit=20&cursor=eyJ...&sort=name" :rel :self}
;; {:url "/api/v1/users?limit=20&cursor=eyJpZCI6IjEyMyI...&sort=name" :rel :next}](build-link-header links)Build RFC 5988 Link header string from link data.
Takes a collection of link entries and produces a single Link header value with multiple relations.
Args: links - Collection of maps with :url and :rel keys :url - Full URL for the link :rel - Relation type (:first, :last, :prev, :next, :self)
Returns: RFC 5988 Link header string
Side Effects:
Example: (build-link-header [{:url "/api/v1/users?offset=0&limit=20" :rel :first} {:url "/api/v1/users?offset=20&limit=20" :rel :next} {:url "/api/v1/users?offset=980&limit=20" :rel :last}]) ;;=> "</api/v1/users?offset=0&limit=20>; rel="first", </api/v1/users?offset=20&limit=20>; rel="next", </api/v1/users?offset=980&limit=20>; rel="last""
Returns: nil if links collection is empty
Build RFC 5988 Link header string from link data.
Takes a collection of link entries and produces a single Link header value
with multiple relations.
Args:
links - Collection of maps with :url and :rel keys
:url - Full URL for the link
:rel - Relation type (:first, :last, :prev, :next, :self)
Returns:
RFC 5988 Link header string
Side Effects:
- String building
- Logging
Example:
(build-link-header
[{:url "/api/v1/users?offset=0&limit=20" :rel :first}
{:url "/api/v1/users?offset=20&limit=20" :rel :next}
{:url "/api/v1/users?offset=980&limit=20" :rel :last}])
;;=> "</api/v1/users?offset=0&limit=20>; rel=\"first\", </api/v1/users?offset=20&limit=20>; rel=\"next\", </api/v1/users?offset=980&limit=20>; rel=\"last\""
Returns:
nil if links collection is empty(build-offset-links base-path params pagination-meta)Build link data for offset-based pagination.
Generates URLs for first, last, prev, next, and self relations based on offset pagination metadata.
Args: base-path - Base URL path (e.g., "/api/v1/users") params - Current query parameters (map) pagination-meta - Offset pagination metadata with keys: :total - Total number of items :offset - Current offset :limit - Items per page :has-next - Boolean indicating if next page exists :has-prev - Boolean indicating if previous page exists :next-offset - Offset for next page (if has-next) :prev-offset - Offset for previous page (if has-prev)
Returns: Vector of link data maps [{:url ... :rel ...} ...]
Side Effects:
Example: (build-offset-links "/api/v1/users" {:limit 20 :offset 40 :sort "name"} {:total 100 :offset 40 :limit 20 :has-next true :has-prev true :next-offset 60 :prev-offset 20 :total-pages 5 :current-page 3}) ;;=> [{:url "/api/v1/users?limit=20&offset=0&sort=name" :rel :first} ;; {:url "/api/v1/users?limit=20&offset=20&sort=name" :rel :prev} ;; {:url "/api/v1/users?limit=20&offset=40&sort=name" :rel :self} ;; {:url "/api/v1/users?limit=20&offset=60&sort=name" :rel :next} ;; {:url "/api/v1/users?limit=20&offset=80&sort=name" :rel :last}]
Build link data for offset-based pagination.
Generates URLs for first, last, prev, next, and self relations based on
offset pagination metadata.
Args:
base-path - Base URL path (e.g., "/api/v1/users")
params - Current query parameters (map)
pagination-meta - Offset pagination metadata with keys:
:total - Total number of items
:offset - Current offset
:limit - Items per page
:has-next - Boolean indicating if next page exists
:has-prev - Boolean indicating if previous page exists
:next-offset - Offset for next page (if has-next)
:prev-offset - Offset for previous page (if has-prev)
Returns:
Vector of link data maps [{:url ... :rel ...} ...]
Side Effects:
- URL building
- Logging
Example:
(build-offset-links
"/api/v1/users"
{:limit 20 :offset 40 :sort "name"}
{:total 100
:offset 40
:limit 20
:has-next true
:has-prev true
:next-offset 60
:prev-offset 20
:total-pages 5
:current-page 3})
;;=> [{:url "/api/v1/users?limit=20&offset=0&sort=name" :rel :first}
;; {:url "/api/v1/users?limit=20&offset=20&sort=name" :rel :prev}
;; {:url "/api/v1/users?limit=20&offset=40&sort=name" :rel :self}
;; {:url "/api/v1/users?limit=20&offset=60&sort=name" :rel :next}
;; {:url "/api/v1/users?limit=20&offset=80&sort=name" :rel :last}](build-query-string params)Build URL query string from parameters map.
Filters out nil values and URL-encodes parameter values.
Args: params - Map of query parameters
Returns: Query string (without leading '?')
Side Effects:
Example: (build-query-string {:limit 20 :offset 40 :sort "name"}) ;;=> "limit=20&offset=40&sort=name"
(build-query-string {:limit 20 :offset nil}) ;;=> "limit=20"
Build URL query string from parameters map.
Filters out nil values and URL-encodes parameter values.
Args:
params - Map of query parameters
Returns:
Query string (without leading '?')
Side Effects:
- URL encoding
- String building
Example:
(build-query-string {:limit 20 :offset 40 :sort "name"})
;;=> "limit=20&offset=40&sort=name"
(build-query-string {:limit 20 :offset nil})
;;=> "limit=20"(build-url base-path params)Build full URL from base path and query parameters.
Args: base-path - Base URL path (e.g., "/api/v1/users") params - Map of query parameters
Returns: Full URL with query string
Side Effects:
Example: (build-url "/api/v1/users" {:limit 20 :offset 40}) ;;=> "/api/v1/users?limit=20&offset=40"
(build-url "/api/v1/users" {}) ;;=> "/api/v1/users"
Build full URL from base path and query parameters.
Args:
base-path - Base URL path (e.g., "/api/v1/users")
params - Map of query parameters
Returns:
Full URL with query string
Side Effects:
- URL encoding (via build-query-string)
- String building
Example:
(build-url "/api/v1/users" {:limit 20 :offset 40})
;;=> "/api/v1/users?limit=20&offset=40"
(build-url "/api/v1/users" {})
;;=> "/api/v1/users"(generate-link-header base-path params pagination-meta)Generate RFC 5988 Link header for pagination response.
Automatically detects pagination type (offset or cursor) and generates appropriate links.
Args: base-path - Base URL path (e.g., "/api/v1/users") params - Current query parameters (map) pagination-meta - Pagination metadata (either offset or cursor type) Must have :type key with value "offset" or "cursor"
Returns: RFC 5988 Link header string, or nil if links are empty
Side Effects:
Example (offset): (generate-link-header "/api/v1/users" {:limit 20 :offset 40} {:type "offset" :total 100 :offset 40 :limit 20 :has-next true :has-prev true :next-offset 60 :prev-offset 20}) ;;=> "</api/v1/users?limit=20&offset=0>; rel="first", ..."
Example (cursor): (generate-link-header "/api/v1/users" {:limit 20 :cursor "eyJ..."} {:type "cursor" :limit 20 :has-next true :next-cursor "eyJpZCI..."}) ;;=> "</api/v1/users?limit=20&cursor=eyJpZCI...>; rel="next", ..."
Generate RFC 5988 Link header for pagination response.
Automatically detects pagination type (offset or cursor) and generates
appropriate links.
Args:
base-path - Base URL path (e.g., "/api/v1/users")
params - Current query parameters (map)
pagination-meta - Pagination metadata (either offset or cursor type)
Must have :type key with value "offset" or "cursor"
Returns:
RFC 5988 Link header string, or nil if links are empty
Side Effects:
- Link building (URL construction, string building)
- Logging
Example (offset):
(generate-link-header
"/api/v1/users"
{:limit 20 :offset 40}
{:type "offset"
:total 100
:offset 40
:limit 20
:has-next true
:has-prev true
:next-offset 60
:prev-offset 20})
;;=> "</api/v1/users?limit=20&offset=0>; rel=\"first\", ..."
Example (cursor):
(generate-link-header
"/api/v1/users"
{:limit 20 :cursor "eyJ..."}
{:type "cursor"
:limit 20
:has-next true
:next-cursor "eyJpZCI..."})
;;=> "</api/v1/users?limit=20&cursor=eyJpZCI...>; rel=\"next\", ..."cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |