Pagination¶
Guidance
- All endpoints that return collections should support pagination, even if they initially only return a few items.
- Metadata about how the resource is paginated should be returned in a separate object (e.g.
meta
in JSON:API).
Pagination allows consumers to retrieve large amounts of data in smaller, more manageable chunks, reducing response times for requests and improving the overall performance of the API. Implementing pagination will help keep your API scalable, as pagination has proven to be an effective strategy to manage increasingly large data sets.
If your endpoint returns a collection, consider including pagination from the start. Data tends to grow rather than shrink, and small data sets will unlikely remain so. Adding pagination after the fact can break the API's behavior (making it no longer backward compatible) and confuse consumers who may assume they have received a complete result when they have only received the first page.
When implementing pagination in your API, separating navigation links and metadata from data is important. That way, consuming applications can, in turn, keep data parsing and rendering separate from pagination logic.
Data specifications and pagination¶
One of the main reasons we recommend using a data convention such as JSON::API and FHIR is that they include pagination as part of their specifications. Following the data specification's pagination schema will save time and reduces bike-shedding amongst teams.
JSON:API specification¶
According to the JSON:API specification, pagination can be implemented using query parameters,
typically page[number]
and page[size]
. Below is an overview of how this can be done for a
collection of Prescription
resources.
The API consumer requests a specific page of Prescription
resources by providing the page[number]
and page[size]
query parameters in the URL. For example:
GET /rx/v0/prescriptions?page[number]=1&page[size]=10
The API server processes the request, retrieves the requested data, and returns a response
containing the Prescription
resources for the specified page. The response includes
pagination details as part of the links object to help the consumer navigate between pages.
Metadata about the total number of records and pages is included in the meta object:
{
"data": [
{
"type": "Prescription",
"id": "1c2dfaaa-4eb9-482e-86a9-4e7274975967",
"attributes": {
"prescriptionNumber": "1239876",
"prescriptionName": "IBUPROFEN 400MG TAB",
"facilityName": "DAYT29",
"stationNumber": "989",
"orderedDate": "2049-07-21T01:39:00Z",
"expirationDate": "2050-07-21T01:39:00Z",
"dispensedDate": "2049-07-22T010:07:00Z",
"quantity": 30,
"isRefillable": true
}
},
{
"type": "Prescription",
"id": "ac9d4b3f-e4bd-49dd-b794-64ad05480729",
"attributes": {
"prescriptionNumber": "1239832",
"prescriptionName": "ACETAMINOPHEN 200MG TAB",
"facilityName": "DAYT29",
"stationNumber": "989",
"orderedDate": "2049-07-22T11:23:00Z",
"expirationDate": "2050-07-22T11:30:00Z",
"dispensedDate": "2049-07-23T012:35:00Z",
"quantity": 30,
"isRefillable": true
}
},
// ... more prescription resources
],
"links": {
"self": "https://api.va.gov/rx/v0/prescriptions?page[number]=1&page[size]=10",
"first": "https://api.va.gov/rx/v0/prescriptions?page[number]=1&page[size]=10",
"prev": null,
"next": "https://api.va.gov/rx/v0/prescriptions?page[number]=2&page[size]=10",
"last": "https://api.va.gov/rx/v0/prescriptions?page[number]=5&page[size]=10"
},
"meta": {
"pagination": {
"pageNumber": 1,
"pageSize": 10,
"pages": 5,
"records": 50
}
}
}
FHIR specification¶
FHIR provides its own approach to pagination, which is different from the JSON:API specification.
The example below shows how pagination can be implemented in FHIR for a collection of MedicationRequest
resources.
The consumer requests a collection of MedicationRequest
resources by providing the _count
query
parameter in the URL:
GET /MedicationRequest?_count=10
The FHIR server processes the request, retrieves the requested data, and returns a Bundle resource containing the
MedicationRequest
resources for the first page. The response also includes pagination links as part of
the link array to provide navigation capabilities between pages:
{
"resourceType": "Bundle",
"type": "searchset",
"total": 50,
"link": [
{
"relation": "self",
"url": "https://api.va.gov/fhir/v0/MedicationRequest?_count=10&page=1"
},
{
"relation": "first",
"url": "https://api.va.gov/fhir/v0/MedicationRequest?_count=10&page=1"
},
{
"relation": "previous",
"url": null
},
{
"relation": "next",
"url": "https://api.va.gov/fhir/v0/MedicationRequest?_count=10&page=2"
},
{
"relation": "last",
"url": "https://api.va.gov/fhir/v0/MedicationRequest?_count=10&page=5"
}
],
"entry": [
{
"fullUrl": "https://example.com/fhir/MedicationRequest/67890",
"resource": {
"resourceType": "MedicationRequest",
"id": "67890"
// ... more MedicationRequest resource properties ...
}
}
]
}
Best practices¶
Outlined below, are a few best practices that should be followed when designing pagination for your API.
Guidance
- Remember to set default and maximum values for the page size.
- If your API does not currently support pagination, introduce it in a way that maintains backward compatibility or you must version your API.