Define pagination guidelines
Co-Authored-By: Ron De Rose <ronald.de.rose@intel.com> Co-Authored-By: Ian Cordasco <graffatcolmingov@gmail.com> Change-Id: I0daac580155e98555a3775de4cb3923d519df503
This commit is contained in:
@@ -8,10 +8,138 @@ and sorting capabilities in a project's public REST API.
|
|||||||
Pagination
|
Pagination
|
||||||
----------
|
----------
|
||||||
|
|
||||||
**TODO** Discuss the methods currently used to paginate in OpenStack APIs.
|
Pagination can be implemented using one or both of two query parameters:
|
||||||
|
|
||||||
**TODO** Make a proposal/decision on the recommended way to implement
|
- ``limit`` to define the number of items returned in the response, and
|
||||||
pagination.
|
|
||||||
|
- ``marker`` to specify the ID of the last seen item
|
||||||
|
|
||||||
|
Note that the marker need not always be the ID/UUID field of the record; it can
|
||||||
|
be any field or combination of fields that will uniquely identify that record.
|
||||||
|
Using a marker that is not unique will result in overlap or skipped records
|
||||||
|
between paged results.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
GET /app/items?limit=30
|
||||||
|
|
||||||
|
Would return *at most* 30 items::
|
||||||
|
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "719aae5f70db4364850f6198ea874aa6",
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "quux",
|
||||||
|
"size": 9
|
||||||
|
},
|
||||||
|
...
|
||||||
|
{
|
||||||
|
"id": "08ec231f6d9a43dda97d4b950c3393df",
|
||||||
|
"foo": "buzz",
|
||||||
|
"baz": "honk",
|
||||||
|
"size": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
If, we then wanted to request the next 30 items after the last one, we would
|
||||||
|
do::
|
||||||
|
|
||||||
|
GET /app/items?limit=30&marker=08ec231f6d9a43dda97d4b950c3393df
|
||||||
|
|
||||||
|
This would return the next (at most) 30 items after the item with ID
|
||||||
|
``08ec231f6d9a43dda97d4b950c3393df``.
|
||||||
|
|
||||||
|
The ability to page through results implies that the items are sorted in a
|
||||||
|
consistent fashion in each request, even if that order is nothing more than the
|
||||||
|
order the items were added to the dataset. If the order of the results changes
|
||||||
|
between requests, the returned pages will not have a meaningful relation to
|
||||||
|
each other.
|
||||||
|
|
||||||
|
A similar consideration would be how to handle the situation when the item
|
||||||
|
whose ID is the ``marker`` value is deleted in between requests. In that event,
|
||||||
|
the response should start with the next item logically. The definition of
|
||||||
|
"logical" is necessarily fuzzy, and will depend on how the data is sorted.
|
||||||
|
There may be some cases, however, where it is not reasonable to try to
|
||||||
|
determine what the next logical item would be. In those cases, a **400 Bad
|
||||||
|
Request** response should be returned, with a clear explanation in the error
|
||||||
|
message that the requested marker value does not exist.
|
||||||
|
|
||||||
|
It is also helpful to users if services generate pagination links and include
|
||||||
|
them in the :ref:`links` portion of the response body. Providing the following
|
||||||
|
link types will make pagination navigation easier:
|
||||||
|
|
||||||
|
- first
|
||||||
|
- prev
|
||||||
|
- self
|
||||||
|
- next
|
||||||
|
- last
|
||||||
|
|
||||||
|
It is important to note that unless the data being paged is static, these links
|
||||||
|
cannot be guaranteed to be accurate. For example, if some items are deleted,
|
||||||
|
the ``prev`` link might contain some items from the current result.
|
||||||
|
|
||||||
|
For example, a response to::
|
||||||
|
|
||||||
|
GET /app/items?limit=30&marker=752b0b9997f24be49e5a1d89d1c53279
|
||||||
|
|
||||||
|
Would look more akin to::
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "719aae5f70db4364850f6198ea874aa6",
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "quux",
|
||||||
|
"size": 9
|
||||||
|
},
|
||||||
|
...
|
||||||
|
{
|
||||||
|
"id": "08ec231f6d9a43dda97d4b950c3393df",
|
||||||
|
"foo": "buzz",
|
||||||
|
"baz": "honk",
|
||||||
|
"size": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "self",
|
||||||
|
"href": "http://example.com/app/items?limit=30&marker=752b0b9997f24be49e5a1d89d1c53279",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "first",
|
||||||
|
"href": "http://example.com/app/items?limit=30",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "prev",
|
||||||
|
"href": "http://example.com/app/items?limit=30&marker=eff79f5b4f8743caa1f775846302c1d5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "next",
|
||||||
|
"href": "http://example.com/app/items?limit=30&marker=08ec231f6d9a43dda97d4b950c3393df",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "last",
|
||||||
|
"href": "http://example.com/app/items?limit=30&marker=6835afb7ea29491bb2722c6c43f1f070",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Alternatively, if services are not including JSON `Hyper-Schema`_ links in
|
||||||
|
their responses they can consider using the ``Link`` header as defined in
|
||||||
|
:rfc:`5988` and :rfc:`6903`.
|
||||||
|
|
||||||
|
When using links, the links that are included change based on which page the
|
||||||
|
user requested. For example, if the user has requested the first page, then it
|
||||||
|
still makes sense to include ``first``, ``self``, ``next``, and ``last`` but
|
||||||
|
not ``prev``. Likewise if it is the last page, then including ``next`` is
|
||||||
|
optional but the rest (``first``, ``prev``, ``self``, ``last``) is sensible.
|
||||||
|
|
||||||
|
It should also be emphasized that calculating the ``last`` link can be costly.
|
||||||
|
In many cases, such link calculation would require querying the entire dataset.
|
||||||
|
Therefore implementing the ``last`` link is optional.
|
||||||
|
|
||||||
Filtering
|
Filtering
|
||||||
---------
|
---------
|
||||||
@@ -243,3 +371,7 @@ and 'sort_dir' query string parameters, see [1]. As these projects adopt these
|
|||||||
guidelines, they should deprecate the older parameters appropriately.
|
guidelines, they should deprecate the older parameters appropriately.
|
||||||
|
|
||||||
[1]: https://wiki.openstack.org/wiki/API_Working_Group/Current_Design/Sorting
|
[1]: https://wiki.openstack.org/wiki/API_Working_Group/Current_Design/Sorting
|
||||||
|
|
||||||
|
|
||||||
|
.. _Hyper-Schema:
|
||||||
|
http://json-schema.org/latest/json-schema-hypermedia.html
|
||||||
|
|||||||
Reference in New Issue
Block a user