-
Notifications
You must be signed in to change notification settings - Fork 0
docs: ETU-74448: Updated guidelines on pagination. #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+136
−11
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
17db15d
docs: ETU-74448: Updated guidelines on pagination.
rikard-swahn fad87ac
docs: ETU-74448: Stricter requirements.
rikard-swahn c8d1ea1
docs: ETU-74448: Stricter requirements.
rikard-swahn 450368f
docs: ETU-74448: Stricter requirements, and typo fixes.
rikard-swahn 4e73cab
docs: ETU-74448: Typo.
rikard-swahn d54aae0
docs: ETU-74448: Updated guidelines on pagination.
rikard-swahn cb42557
docs: ETU-74448: Updated guidelines on pagination.
rikard-swahn 7dc9cc6
docs: ETU-74448: Moved guidelines.md back to root.
rikard-swahn 9e19139
docs: ETU-74448: Clarification on embedding paging, filtering etc in …
rikard-swahn da33575
docs: ETU-74448: Clarification on cursor pagingation preference.
rikard-swahn 78abfea
docs: ETU-74448: Renamed param "size" -> "pageSize"
rikard-swahn e8e9339
docs: ETU-74448: MUST be named items
rikard-swahn 4fce6b7
docs: ETU-74448: Renamed totalCount -> totalItems
rikard-swahn b7c47ef
docs: ETU-74448: Added pageSize and totalPages response fields.
rikard-swahn bdfe5be
docs: ETU-74448: Corrected description of cursor: it does not have to…
rikard-swahn 723fae1
docs: ETU-74448: Improved cursor description.
rikard-swahn 65b7d5d
docs: ETU-74448: Corrected description of cursor.
rikard-swahn e0614a3
docs: ETU-74448: Added examplpes for "Cursor key selection" and simpl…
rikard-swahn b7888ae
docs: ETU-74448: Improved Encoding section with regards to Base64 and…
rikard-swahn 0909a90
docs: ETU-74448: Removed hasMore from cursor response format.
rikard-swahn 57f633d
docs: ETU-74448: Removed NoSQL-line in Choosing a Strategy, not reall…
rikard-swahn c68cc48
docs: ETU-74448: Revised rule of thumb text for Choosing a Strategy.
rikard-swahn 1cb71ff
docs: ETU-74448: Offset pagination use offset + limit instead of page…
rikard-swahn 233b28e
docs: ETU-74448: Cleanup
rikard-swahn 17c9ab3
docs: ETU-74448: Added SHOULD about default and max for limit.
rikard-swahn f28cca4
docs: ETU-74448: Corrected pagination parameter names.
rikard-swahn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| # Pagination and Sorting | ||
|
|
||
| ## Pagination | ||
|
|
||
| When implementing pagination, you **MUST** use either Cursor Pagination (preferred) or Offset Pagination, on the formats detailed below. | ||
|
|
||
| ### Offset Pagination | ||
|
|
||
| This strategy is based on these query parameters: | ||
|
|
||
| | Parameter | Type | Description | | ||
| |-----------|---------|----------------------------------------------------------------------------| | ||
| | `offset` | integer | Zero-based index of the first item to retrieve. **MUST** be named `offset` | | ||
| | `limit` | integer | Number of items to get. **MUST** be named `limit` | | ||
|
|
||
| Implementations **SHOULD** implement and document default and max values for `limit`. | ||
|
|
||
| **Example request:** | ||
|
|
||
| ```http | ||
| GET /api/v1/bus-stops?city=Oslo&offset=10&limit=20 | ||
| ``` | ||
|
|
||
| #### Response format | ||
|
|
||
| The response **MUST** contain the following fields: | ||
|
|
||
| | Parameter | Type | Description | | ||
| |--------------|---------|----------------------------------------------------------------------------| | ||
| | `items` | array | **MUST** be named `items`. | | ||
| | `totalItems` | integer | The total number of items across all pages. **MUST** be named `totalItems` | | ||
| | `limit` | integer | The requested `limit`, or max limit if given `limit` was over max. | | ||
|
|
||
| ### Cursor / Keyset Pagination | ||
|
|
||
| This strategy is based on these query parameters: | ||
|
|
||
| | Parameter | Type | Description | | ||
| |-----------|---------|----------------------------------------------------------------------------------------| | ||
| | `cursor` | string | An opaque string identifying the next page of items to get. **MUST** be named `cursor` | | ||
| | `pageSize` | integer | Number of items per page. **MUST** be named `pageSize` | | ||
|
|
||
| Cursor-based pagination is based on a `cursor` that is created when handling requests from the client. The cursor is returned to the client in the response body. | ||
| The cursor points to the next page of items. Sorting parameters, `pageSize` and filters **MAY** also be embedded in the cursor. | ||
|
|
||
| On the next request from the client, the cursor is sent back to the service. | ||
| The service returns the requested items and calculates a new cursor. In this way, the client can paginate through items. | ||
|
|
||
| Clients should not inspect or parse cursors - a cursor should be treated as an opaque string with an unknown and possibly changing format. | ||
|
|
||
| **Example requests:** | ||
|
|
||
| First request (no cursor available to client yet): | ||
| ```http | ||
| GET /api/v1/bus-stops?city=Oslo&pageSize=20 | ||
| ``` | ||
| The response includes a cursor for the next page. To fetch the next page: | ||
| ```http | ||
| GET /api/v1/bus-stops?city=Oslo&pageSize=20&cursor=eyJpZCI6MTAwfQ | ||
| ``` | ||
|
|
||
| #### Cursor key selection | ||
|
|
||
| The cursor **MUST** encode a value (or set of values) that uniquely and stably identifies a position in the sorted result set. | ||
|
|
||
| Example cursor with multiple values: | ||
|
|
||
| ```json | ||
| { | ||
| "id": "fa760939-dacc-4653-be5b-bfe6e87d9fcf", | ||
| "sort": "name" | ||
| } | ||
| ``` | ||
|
|
||
| Example cursor key for encoding a single value (e.g. database id): | ||
| ``` | ||
| 100 | ||
| ``` | ||
|
|
||
|
|
||
| #### Encoding | ||
| The cursor **MUST** be URL-safe (no URL-encoding required). Because the cursor should be opaque to the client and may contain internal details, | ||
| it **MAY** be Base64 encoded. For cursors with multiple values, a common solution is to have JSON in string value and then Base64-encode the string. | ||
| If the cursor contains data that you do not want to expose, the cursor **MAY** be encrypted and then Base64 encoded. | ||
|
|
||
| #### Response format | ||
|
|
||
| The response **MUST** contain the following fields: | ||
|
|
||
| | Parameter | Type | Description | | ||
| |-----------|---------|-------------------------------------------------------------------------------------------------------------------------------------| | ||
| | `items` | array | **MUST** be named `items`. | | ||
| | `cursor` | string | An opaque string pointing to next item to get. If no more items, cursor value is not returned to client. **MUST** be named `cursor` | | ||
|
|
||
|
|
||
| ### Choosing a Strategy | ||
|
|
||
| Use the comparison table below to select the pagination strategy that best fits your use case. | ||
|
|
||
| | Criterion | Offset Pagination | Cursor Pagination | | ||
| |------------------------------------|--------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------| | ||
| | **Ease of use** | Widely understood; broad framework support | Less familiar to most clients; a bit more work on the server side | | ||
| | **Jump to arbitrary position** | ✅ Supported | ❌ Not supported — only sequential traversal | | ||
| | **Consistency under data changes** | ⚠️ Inserts/deletes between requests may cause duplicates or missing items | ✅ Stable — cursor anchors position in the data set | | ||
| | **Performance on large data sets** | ⚠️ `OFFSET` queries degrade as offset gets bigger, because the database must scan and discard all rows before the offset | ✅ Constant-time lookups | | ||
|
|
||
| As a rule of thumb, cursor pagination **SHOULD** be used unless: offset pagination DB queries are not too heavy and inserts and deletes are infrequent OR jumping to a specific position must be supported. | ||
|
|
||
| ## Sorting | ||
| Sorting **MAY** be implemented without pagination, but when using pagination you **MUST** also use sorting. | ||
|
|
||
| :eyes: If you implement sorting, you **MUST** use query parameter `sort`. | ||
| You **MAY** also allow sorting on multiple levels, and allow specifying sort order (desc / asc). | ||
| In your service, always use a secondary sorting on a unique id, so that two entries with the same primary sorting | ||
| (e.g. created date) are always sorted in the same order. | ||
|
|
||
| Example: | ||
| ```http | ||
| GET /api/v1/bus-stops?city=Oslo&sort=name,asc&sort=something,desc | ||
| ``` | ||
|
rikard-swahn marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.