diff --git a/_config.yml b/_config.yml
index 795ecc305..771a57edc 100644
--- a/_config.yml
+++ b/_config.yml
@@ -13,11 +13,11 @@ whitelist:
- jekyll-sitemap
- jekyll-feed
- jekyll-seo-tag
- - jekyll-gist
- jemoji
incremental: false
gist:
noscript: false
+
# customize github pages
quiet: false
markdown: kramdown
@@ -25,7 +25,9 @@ highlighter: rouge
kramdown:
input: GFM
hard_wrap: false
- auto_ids: false
+ auto_ids: true
+ auto_id_prefix: auto-id-
+ auto_id_stripping: true
template: '' # cannot customize
math_engine: mathjax # cannot customize
syntax_highligher: rouge # cannot customize
@@ -48,6 +50,9 @@ port: 9876
collections:
format:
output: true
+ profiles:
+ output: true
+ permalink: /:collection/:path
defaults:
- scope:
@@ -57,6 +62,20 @@ defaults:
layout: page
show_sidebar: true
is_spec_page: true
+ - scope:
+ path: ""
+ type: "profiles"
+ values:
+ layout: profile_error
+ is_spec_page: false
+ show_sidebar: false
+ - scope:
+ path: "*/*/*/index.md"
+ type: "profiles"
+ values:
+ layout: profile
+ is_spec_page: false
+ show_sidebar: true
latest_version: 1.0
excerpt_separator: ""
@@ -84,3 +103,12 @@ quicklinks:
url: /format/
- title: Contribute on GitHub
url: https://github.com/json-api/json-api
+
+profile_categories:
+ - Pagination
+ # these are some other potential categories.
+ # Uncomment them if you're adding a profile in one of these categories.
+ # - Filtering
+ # - Actions/Hypermedia
+ # - Data Modeling
+ # - Deep Querying
diff --git a/_format/1.1/index.md b/_format/1.1/index.md
index 915f7e5b7..d263c6878 100644
--- a/_format/1.1/index.md
+++ b/_format/1.1/index.md
@@ -234,7 +234,7 @@ other and with `type` and `id`. In other words, a resource can not have an
attribute and relationship with the same name, nor can it have an attribute
or relationship named `type` or `id`.
-#### Attributes
+##### Attributes
The value of the `attributes` key **MUST** be an object (an "attributes
object"). Members of the attributes object ("attributes") represent information
@@ -253,7 +253,7 @@ alongside other information to be represented in a resource object, these keys
> Note: See [fields] and [member names] for more restrictions on this container.
-#### Relationships
+##### Relationships
The value of the `relationships` key **MUST** be an object (a "relationships
object"). Members of the relationships object ("relationships") represent
@@ -285,7 +285,7 @@ data, not the related resources.
> Note: See [fields] and [member names] for more restrictions on this container.
-#### Related Resource Links
+##### Related Resource Links
A "related resource link" provides access to [resource objects][resource objects] [linked][links]
in a [relationship][relationships]. When fetched, the related resource object(s)
@@ -300,7 +300,7 @@ relationship isn't currently associated with any target resources. Additionally,
a related resource link **MUST NOT** change because its relationship's content
changes.
-#### Resource Linkage
+##### Resource Linkage
Resource linkage in a [compound document] allows a client to link together all
of the included [resource objects] without having to `GET` any URLs via [links].
@@ -1920,10 +1920,10 @@ request is received, a server **SHOULD** attempt to apply the requested profiles
to its response.
For example, in the following request, the client asks that the server apply the
-`http://jsonapi.org/extensions/last-modified` profile if it is able to.
+`http://example.com/last-modified` profile if it is able to.
```http
-Accept: application/vnd.api+json;profile="http://example.com/extensions/last-modified", application/vnd.api+json
+Accept: application/vnd.api+json;profile="http://example.com/last-modified", application/vnd.api+json
```
> Note: The second instance of the JSON:API media type in the example above is
@@ -2252,6 +2252,11 @@ supported as well.
3. alter the JSON structure of any concept defined in this specification,
including to allow a superset of JSON structures.
+
+> If you create your own profile, you are **strongly encouraged to [register](/extensions/#profile-registration)
+> it** with the JSON API [profile registry](/extensions/), so that others can
+> find and reuse it.
+
#### Revising a Profile
Profiles **MAY** be revised over time, e.g., to add new capabilities. However,
diff --git a/_includes/global_head_assets.html b/_includes/global_head_assets.html
new file mode 100644
index 000000000..22551095d
--- /dev/null
+++ b/_includes/global_head_assets.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_includes/global_html_footer.html b/_includes/global_html_footer.html
new file mode 100644
index 000000000..78938024e
--- /dev/null
+++ b/_includes/global_html_footer.html
@@ -0,0 +1,23 @@
+
+
diff --git a/_includes/header_offset_2.md b/_includes/header_offset_2.md
new file mode 100644
index 000000000..221a039f5
--- /dev/null
+++ b/_includes/header_offset_2.md
@@ -0,0 +1,33 @@
+{% comment %}
+ When we embed the markdown from a user-provided profile specification
+ inside the profile layout, we need to increment each heading level by 2.
+ Kramdown, our markdown parser, provides a `header_offset` option, but
+ Jekyll only allows us to set that globally -- and setting it globally
+ to two would break our other pages. So, this include lets us take arbitrary
+ HTML (from rendered markdown) and does some liquid string replacements
+ to offset its headings. This is pretty janky (even more so because liquid
+ only allows us to do literal string replacement, not regex replacement),
+ but I think it *should* work robustly, thanks to the fact that angle
+ brackets (i.e., `<` and `>`) aren't supposed to appear in HTML unencoded,
+ and I imagine Kramdown respects that.
+{% endcomment %}
+{{ include.content
+ | replace: "
+ {% include global_html_footer.html %}
+
+
diff --git a/_layouts/profile_error.html b/_layouts/profile_error.html
new file mode 100644
index 000000000..cbe0ea42d
--- /dev/null
+++ b/_layouts/profile_error.html
@@ -0,0 +1,28 @@
+
+
+
+ {% include global_head_assets.html %}
+ Codestin Search App
+
+
+
+ {% include site_navigation.html %}
+
+ {% if page.show_sidebar %}
+
+
+
+ {% endif %}
+
+
{{ page.title }}
+
+ {{ content }}
+
+
+
+ {% include global_html_footer.html %}
+
+
+
diff --git a/_profiles/ethanresnick/cursor-pagination/index.md b/_profiles/ethanresnick/cursor-pagination/index.md
new file mode 100644
index 000000000..e11aa68bb
--- /dev/null
+++ b/_profiles/ethanresnick/cursor-pagination/index.md
@@ -0,0 +1,610 @@
+---
+name: Cursor Pagination
+short_description: |
+ Enables pagination forward and backwards from a client-provided cursor, with
+ a client-controlled page size.
+
+extended_description: |
+ Cursor-based pagination (aka keyset pagination) is a [common](https://slack.engineering/evolving-api-pagination-at-slack-1c1f644f8e12)
+ [pagination strategy](https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/)
+ that avoids many of the pitfalls of "offset–limit" pagination.
+
+ For example, with offset–limit pagination, if an item from a prior page is
+ deleted while the client is paginating, all subsequent results will be shifted
+ forward by one. Therefore, when the client requests the next page, there's one
+ result that it will skip over and never see. Conversely, if a result is added
+ to the list of results as the client is paginating, the client may see the
+ same result multiple times, on different pages. Cursor-based pagination
+ can prevent both of these possibilities.
+
+ Cursor-based pagination also performs better for large data sets under most
+ implementations.
+
+ To support cursor-based pagination, this specification defines three query
+ parameters — `page[size]`, `page[before]`, and `page[after]` —
+ and a method for providing clients with pagination links and cursors in response
+ bodies.
+
+ For example, this request would get the next 100 people after the cursor `abcde`:
+
+ ```
+ GET /people?page[size]=100&page[after]=abcde
+ ```
+
+ Replacing `page[after]` with `page[before]` would allow the client to paginate
+ backwards.
+
+ Alternatively, to find all people between cursors `abcde` and `fghij`
+ (exclusive), the client could request:
+
+ ```
+ GET /people?page[after]=abcde&page[before]=fghij
+ ```
+
+ Other combinations are possible, and these parameters are described more below.
+
+# If your profile defines values that can only be used in document members that
+# were introduced after JSON:API v1.0, change the minimum_jsonapi_version field
+# and fill in the minimum_jsonapi_version_explanation field with an explanation
+# of the features you're relying on from the JSON:API version you've indicated.
+minimum_jsonapi_version: 1.0
+minimum_jsonapi_version_explanation:
+
+# Url of a Github repo or some other place where people can
+# ask questions/start discussions about your extension.
+discussion_url: http://tets.com
+
+author_name: Ethan Resnick
+author_email: ethan.resnick@gmail.com
+# Optional fields
+author_website: https://ethanresnick.com/
+author_phone: +13104398032 # use `tel` url format.
+
+# Valid categories are listed under the profile_categories section
+# in https://github.com/json-api/json-api/blob/gh-pages/_config.yml
+categories:
+ - Pagination
+---
+# Concepts
+
+## Sorting Requirement
+
+**Pagination only applies to an ordered list of results.** This order must not
+change between requests unless the underlying data changes, to ensure that
+results don't arbitrarily move between pages.
+
+If the client's paginated request includes a `?sort` query parameter that
+only partially orders the results, the server **MUST** apply additional
+sorting constraints — consistent with the client-requested ones — to produce
+a unique ordering, if it wishes to support pagination of that data.
+
+For example, suppose a client requests `GET /people?sort=age&page[size]=10`.
+If multiple people have the same age, their relative ordering is not defined
+(and could vary between requests), making pagination impossible. Therefore,
+to fulfill the request, the server must treat all paginated requests with
+`?sort=age` as though the client had instead asked to sort by age followed
+by some unique field or combination of fields (e.g., `?sort=age,id`).
+
+Similarly, when the collection being paginated has no natural or
+client-requested order (like the set of resource identifier objects in
+a relationship), the server **MUST** assign an order if it wishes to support
+pagination.
+
+The server **MAY** reject pagination requests if the client has requested that
+the results be sorted in a way that server cannot efficiently paginate. In that
+case, the server **MUST** reject the request according to the rules for the
+[unsupported sort error].
+
+## Cursors
+A "cursor" is a string, created by the server using whatever method it likes,
+that divides the list of results into those that fall before the cursor, those
+that fall after the cursor, and, optionally, one result that falls "on" the
+cursor.
+
+For example, imagine that the list of results being paginated is as follows:
+
+```
+[
+ { "type": "examples", "id": "1" },
+ { "type": "examples", "id": "5" },
+ { "type": "examples", "id": "7" },
+ { "type": "examples", "id": "8" },
+ { "type": "examples", "id": "9" }
+]
+```
+
+For this list, the server might produce the cursor string `abcde` as its way of
+encoding "id = 5". With that cursor, then, the first result would fall before
+the cursor, the second result would fall on the cursor, and the other results
+would fall after it.
+
+The list of results **MAY** change between the client's pagination requests.
+For example, the result with `"id": "5"` might be removed from the result set if
+the resource it refers to is deleted. In that case, the cursor `abcde` would no
+longer fall on any one result, but the same results would still come before it
+and after it.
+
+In rare cases, a server may find it unacceptable for the results list to change
+between a client's pagination requests. In those cases, the server **MAY**
+encode into the cursor information uniquely identifying the client or its session,
+and use that identifier to return consistent results from a single "snapshot" in
+time.
+
+# Query Parameters
+
+## `page[size]`
+
+The ```page[size]``` parameter indicates the number of results that the client
+would like to see in the response.
+
+If `page[size]` is provided, it **MUST** be a positive integer.[1](#fn-1)
+If this requirement isn't met — e.g. if `page[size]` is negative — the server
+**MUST** respond according to the rules for the [invalid query parameter error].
+
+For each endpoint on which it supports pagintation, a server **MAY** define a
+maximum number of results that it will send in response to a paginated request
+to that endpoint. This is called the "max page size". If the server does not
+choose a max page size for a given endpoint, it is implicitly infinity.
+
+If `page[size]` exceeds the server-defined max page size, the server **MUST**
+respond according to the rules for the [max page size exceeded error].
+
+If `page[size]` is omitted, the server **MUST** choose a "default page size".
+This default size **MUST** be an integer between 1 and the max page size,
+inclusive.
+
+The `page[size]` value, or the default page
+size if `page[size]` is omitted, is called the "used page size".
+
+On any valid paginated request, the number of [pagination items][pagination item]
+returned **MUST** equal the used page size — provided there are at
+least that many items in the results list, and which satisfy the constraints of
+the `page[after]` and/or `page[before]` parameters (if any).
+
+## `page[after]` and `page[before]`
+
+The `page[after]` and `page[before]` parameters are both optional and both, if
+provided, take a cursor as their value. If their value is not a valid cursor,
+the server **MUST** respond according to the rules for the
+[invalid query parameter error].
+
+The `page[after]` parameter is typically sent by the client to get the next page,
+while `page[before]` is used to get the prior page.
+
+More formally, when `page[after]` is provided, the returned [paginated data]
+**MUST** have as its first item the item that is *immediately after* the cursor
+in the results list. (An exception is that, if there are no items in the results
+list that fall after the cursor, the returned paginated data **MUST** be an
+empty array.)
+
+When `page[before]` is provided, the *last* item returned in the [paginated data]
+**MUST** be the item that is closest to, but still before, the cursor in the
+unpaginated results list. (Analogous to the above, if there are no items in the
+results list that fall before the cursor, the returned paginated data **MUST**
+be an empty array.)
+
+For example, imagine that the list of results being paginated is again:
+
+```
+[
+ { "type": "examples", "id": "1" },
+ { "type": "examples", "id": "5" },
+ { "type": "examples", "id": "7" },
+ { "type": "examples", "id": "8" },
+ { "type": "examples", "id": "9" }
+]
+```
+
+Further, imagine that the cursor `xxx` falls on the entry with `"id": "9"`,
+while the cursor `abcde` still falls on the entry with `"id": "5"`.
+
+Then, for example, if the request was:
+
+```
+GET /example-data?page[after]=abcde&page[size]=2
+```
+
+The response would contain:
+
+```
+{
+ "links": {
+ "prev": "/example-data?page[before]=yyy&page[size]=2",
+ "next": "/example-data?page[after]=zzz&page[size]=2"
+ },
+ "data": [
+ // the pagination item metadata is optional below.
+ { "type": "examples", "id": "7", "meta": { "page": { "cursor": "yyy" } } },
+ { "type": "examples", "id": "8", "meta": { "page": { "cursor": "zzz" } } }
+ ]
+}
+```
+
+Alternatively, if the request was:
+
+```
+GET /example-data?page[before]=xxx&page[size]=3
+```
+
+The response would contain:
+
+```
+{
+ "links": {
+ "prev": "/example-data?page[before]=abcde&page[size]=3"
+ "next": "/example-data?page[after]=zzz&page[size]=3"
+ },
+ "data": [
+ // again, optional pagination item metadata is allowed for each item here.
+ { "type": "examples", "id": "5" },
+ { "type": "examples", "id": "7" },
+ { "type": "examples", "id": "8" }
+ ]
+}
+```
+
+Notice that the `page[before]=xxx` is causing the last item in the response's
+[paginated data] to be the entry with `"id": "8"`, while the number of items in
+the paginated data above that item is controlled by the used page size.
+
+### Omitting Both `page[after]` and `page[before]`
+If the client's paginated request includes neither the `page[after]` nor the
+`page[before]` parameters, the returned [paginated data] **MUST** start with
+the first item from the results list. (If the results list is empty, the
+paginated data **MUST** be an empty array.)
+
+### Combining `page[after]` and `page[before]`
+Clients **MAY** use the `page[after]` and `page[before]` parameters together on
+the same request. These are called "range pagination requests", as the client is
+asking for all the results starting from immediately after the `page[after]`
+cursor and continuing up until the `page[before]` cursor.
+
+Servers are not required to support such requests. If the server chooses not to
+support these requests, it **MUST** respond according to the rules for the
+[range pagination not supported error].
+
+On range pagination requests, the server **MUST** use its max page size for that
+endpoint as the default page size. In other words, the [used page size] will
+either by the value of the `page[size]` parameter or the max page size.
+
+If the number of results that satisfy both the `page[after]` and `page[before]`
+constraints exceeds the used page size, the server **MUST** respond with the
+same [paginated data] that it would have if the `page[before]` parameter had not
+been provided. However, in this case the server **MUST** also add
+`"rangeTruncated": true` to the [pagination metadata] to indicate to the client
+that the paginated data does not contain all the results it requested.
+
+For example, given our example data and cursors above, imagine the client
+requests:
+
+```
+GET /example-data?page[after]=abcde&page[before]=xxx
+```
+
+Then, assuming our server's max page size was greater than 1, the response
+would contain:
+
+```
+{
+ "links": {
+ "prev": "/example-data?page[before]=yyy",
+ "next": "/example-data?page[after]=zzz"
+ },
+ "data": [
+ { "type": "examples", "id": "7" },
+ { "type": "examples", "id": "8" }
+ ]
+}
+```
+
+However, if our server's max page size was 1, or the client included
+`page[size]=1` in its request, the response would contain:
+
+```
+{
+ "meta": {
+ "page": { "rangeTruncated": true }
+ },
+ "links": {
+ "prev": "/example-data?page[before]=yyy&page[size]=1",
+ "next": "/example-data?page[after]=yyy&page[size]=1"
+ },
+ "data": [
+ { "type": "examples", "id": "7" }
+ ]
+}
+```
+
+# Document Structure
+
+## Terms
+This profile uses the following terms to refer to different document elements:
+
+1. paginated data: an array in a JSON:API
+ response document that holds the results that were extracted from a full list
+ of results being paginated. It is always the value of a `data` key. When the
+ [primary data](https://jsonapi.org/format/#document-top-level) is being
+ paginated, the value of the document's top-level `data` key is paginated data.
+ When the resource identifier objects in a relationship are being paginated,
+ the value of the `data` key in the relationship object is paginated data.
+
+2. pagination links: the `links` object
+ that's a sibling of the paginated data.
+
+3. pagination metadata: the [`page`
+ member] of the `meta` object that's a sibling of the paginated data (and
+ pagination links).
+
+4. pagination item: an entry in the
+ paginated data array.
+
+5. pagination item metadata: the
+ [`page` member] of the `meta` object that's at the top-level of a
+ paginated data item.
+
+To demonstrate these terms, various elements are labeled in the following
+example:
+
+```
+GET /people?page[size]=1
+```
+```
+{
+ // "pagination links" (for the top-level `data`)
+ "links": { },
+ "meta": {
+ // "pagination metadata"
+ "page": {}
+ },
+ // "paginated data"
+ "data": [
+ // a "pagination item"
+ {
+ "type": "people",
+ "id": "1",
+ // "pagination item metadata" in `page`.
+ "meta": { "page": {} },
+ "attributes": {},
+ "relationships": {
+ "friends": {
+ // "pagination links".
+ // would be non-empty in practice to indicate that the server
+ // has chosen to paginate this relationship, even though the
+ // client hasn't explicitly asked, which is allowed.
+ "links": {},
+ // another instance of "paginated data"
+ "data": [
+ // a "pagination item", with (empty) "pagination item metadata".
+ { "type": "people", "id": "3", "meta": { "page": { } } }
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+
+## `page` Meta Object Members
+This profile reserves a `page` member in every JSON:API-defined `meta` object.
+(Each of these `page` members constitutes an element defined by this profile,
+so they can be [aliased](https://jsonapi.org/format/1.1/#profile-keywords-and-aliases).)
+
+The `page` member, when present, **MUST** hold an object as its value; any other
+values are [unrecognized](https://jsonapi.org/format/1.1/#profiles-processing).
+The recognized keys/values in these various `page` objects are defined throughout
+this specification.
+
+
+## Item Cursors
+The server **MAY** choose to send some or all pagination items back to the client
+with a `cursor` member in the [pagination item's metadata][pagination item metadata].
+If present, this member **MUST** hold a cursor that (at the time of the response)
+["falls on"][cursor] the item it is returned with. Clients can use this cursor
+to paginate from this item.
+
+For example, a response might contain:
+
+```
+{
+ // top-level links, meta, etc. omitted.
+ // more people would likely be in the response as well.
+ "data": [{
+ "type": "people",
+ "id": "3",
+ "meta": {
+ "page": { "cursor": "someOpaqueString" }
+ }
+ //...
+ }]
+}
+```
+
+With this response, clients could use `page[before]=someOpaqueString` or
+`page[after]=someOpaqueString` to paginate from person 3 in either direction.
+
+## Links
+JSON:API allows four types of pagination links: [`prev`, `next`, `first`, and
+`last`](https://jsonapi.org/format/#fetching-pagination).
+
+It is **RECOMMENDED** that servers include `first` and `last` links when these
+are inexpensive to compute.
+
+However, servers **MUST** include a `prev` and a `next` link for each instance
+of paginated data in a response.
+
+If a request does not contain the `page[before]` parameter, the server **MUST**
+determine whether a next page exists, and return `null` as the `next` link if not.
+
+If a request does not contain the `page[after]` parameter, the server **MUST**
+determine whether a previous page exists, and return `null` as the `prev` link
+if not.
+
+In all other cases, a server **SHOULD** set these links to `null` when it can
+inexpensively determine that the current response is for the first or last page
+respectively.
+
+However, if the server can't easily determine whether there are prior results
+(when computing the `prev` link) or subsequent results (when computing the
+`next` link), it **MAY** use a URI in these links that returns an empty array
+as its paginated data.
+
+For example, imagine a request for:
+
+```
+GET /example-data?page[before]=xyz
+```
+
+To fulfill this request, the server will likely issue a query that filters the
+full `example-data` results list to find only records that are before the cursor.
+From those query results, the server would not know whether there are additional
+results that come *after* the cursor, and it may not have a cheap way to find out.
+
+So, in this case, the server can simply return a `next` URI where the `page[after]`
+parameter is set to the [item cursor][item cursors] for the last item in the
+response's paginated data.
+
+If the client goes to fetch this link, it will either receive an empty array
+as the paginated data, in which case it knows it's reached the end, or it will
+get the subsequent results.
+
+> Note: in general, servers are likely to find it more expensive to determine
+whether a previous page exists when `page[after]` is in use, and whether a
+subsequent page exists when `page[before]` is in use. Luckily, when `page[after]`
+is in use, clients usually don't care about the previous page (only the next
+one), and vice-versa when `page[before]` is in use. So, by allowing the server
+to return a link that might end up corresponding to an empty page, the server
+can often skip a query that will never be needed.
+
+## Collection Sizes
+The pagination metadata **MAY** contain a `total` member containing an integer
+indicating the total number of items in the list of results that's being
+paginated.
+
+For example, a response to `GET /people?page[size]=2` might include:
+
+```
+{
+ "meta": {
+ "page": { "total": 200 }
+ },
+ // links omitted
+ "data": [
+ {
+ "type": "people",
+ // ...
+ },
+ {
+ "type": "people",
+ // ...
+ }
+ ]
+}
+```
+
+The pagination metadata **MAY** also contain an `estimatedTotal` member.
+If present, the value of this member **MUST** be an object. That object
+**MAY** have one key, `bestGuess`. If present, `bestGuess` **MUST** contain
+an integer indicating the server's best estimate of the size of the full results
+list.
+
+Server's might choose to use `estimatedTotal` instead of `total` when computing
+the exact total is costly.
+
+# Error Cases
+## Unsupported Sort Error
+The server **MUST** respond to this error by sending a `400 Bad Request`. The
+response document **MUST** contain an error object that identifies the `sort`
+parameter as the [error's `source`](https://jsonapi.org/format/#error-objects),
+and has a `type` link of:
+
+```
+https://jsonapi.org/profiles/ethanresnick/cursor-pagination/unsupported-sort
+```
+
+
+## Max Page Size Exceeded Error
+The server **MUST** respond to this error by sending a `400 Bad Request`. The
+response document **MUST** contain an error object that:
+
+- identifies `page[size]` as the error's `source`;
+- provides the maximum page size as an integer in `maxSize` member of the `page`
+ element in the error object's `meta` object; and
+- includes a `type` link of:
+
+ ```
+ https://jsonapi.org/profiles/ethanresnick/cursor-pagination/max-size-exceeded
+ ```
+
+If this profile's `page` element has not been [aliased](https://jsonapi.org/format/1.1/#profile-keywords-and-aliases),
+the error object might look like:
+
+```
+{
+ "status": "400",
+ "meta": {
+ "page": { "maxSize": 100 }
+ },
+ "title": "Page size requested is too large.",
+ "detail": "You requested a size of 200, but 100 is the maximum.",
+ "source": {
+ "parameter": "page[size]"
+ },
+ "links": {
+ "type": ["https://jsonapi.org/profiles/ethanresnick/cursor-pagination/max-size-exceeded"]
+ }
+}
+```
+
+## Invalid Parameter Value Error
+The server **MUST** respond to this error by sending a `400 Bad Request` with an
+error object in the response document that identifies the problematic parameter
+in the error object's `source` member.
+
+For example, the server might send:
+
+```
+{
+ "errors": [{
+ "title": "Invalid Parameter.",
+ "detail": "page[size] must be a positive integer; got 0",
+ "source": { "parameter": "page[size]" },
+ "status": "400"
+ }]
+}
+```
+
+## Range Pagination Not Supported Error
+The server **MUST** respond to this error by sending a `400 Bad Request`. The
+response document **MUST** contain an error object that has a `type` link of:
+
+```
+https://jsonapi.org/profiles/ethanresnick/cursor-pagination/range-pagination-not-supported
+```
+
+# Notes
+
+1. Technically, a URI is a series of characters, so the value
+ of a query parameter -- including `page[size]` -- is always a string. When
+ the text says the value "MUST be a positive integer", it means more precisely
+ that the value **MUST** be a sequence of characters matching the regular
+ expression `^[0-9]+$`, which **MUST** then be interpreted as a base-10 integer.
+
+
+[sorting requirement]: #concepts-sorting
+[cursor]: #concepts-cursors
+
+[`page` member]: #document-page-meta
+[item cursors]: #document-item-cursors
+
+[range pagination]: #query-range-pagination
+
+[used page size]: #terms-used-page-size
+[paginated data]: #terms-paginated-data
+[pagination metadata]: #terms-pagination-metadata
+[pagination item]: #terms-pagination-item
+[pagination item metadata]: #terms-pagination-item-metadata
+
+[unsupported sort error]: #errors-unsupported-sort
+[max page size exceeded error]: #errors-max-page-size-exceeded
+[invalid query parameter error]: #errors-invalid-query-parameter
+[range pagination not supported error]: #errors-range-pagination-not-supported
diff --git a/_profiles/ethanresnick/cursor-pagination/max-size-exceeded.md b/_profiles/ethanresnick/cursor-pagination/max-size-exceeded.md
new file mode 100644
index 000000000..b7a9f9267
--- /dev/null
+++ b/_profiles/ethanresnick/cursor-pagination/max-size-exceeded.md
@@ -0,0 +1,3 @@
+---
+redirect_to: /profiles/ethanresnick/cursor-pagination/#auto-id-pagesize
+---
diff --git a/_profiles/ethanresnick/cursor-pagination/range-pagination-not-supported.md b/_profiles/ethanresnick/cursor-pagination/range-pagination-not-supported.md
new file mode 100644
index 000000000..21d10b38e
--- /dev/null
+++ b/_profiles/ethanresnick/cursor-pagination/range-pagination-not-supported.md
@@ -0,0 +1,3 @@
+---
+redirect_to: /profiles/ethanresnick/cursor-pagination/#query-range-pagination
+---
diff --git a/_profiles/ethanresnick/cursor-pagination/unsupported-sort.md b/_profiles/ethanresnick/cursor-pagination/unsupported-sort.md
new file mode 100644
index 000000000..1507442a1
--- /dev/null
+++ b/_profiles/ethanresnick/cursor-pagination/unsupported-sort.md
@@ -0,0 +1,3 @@
+---
+redirect_to: /profiles/ethanresnick/cursor-pagination/#concepts-sorting
+---
diff --git a/extensions/index.md b/extensions/index.md
index 3bdb5e93a..4ea22502d 100644
--- a/extensions/index.md
+++ b/extensions/index.md
@@ -2,22 +2,118 @@
layout: page
title: Extensions
show_sidebar: true
+redirect_from: /profiles
---
-## Status
+JSON:API can be extended with profiles. These profiles enable an API to
+provide clients with information or functionality beyond that described
+in the base JSON:API specification.
-**An extension system is currently under development,** and you can view the
-latest work [here](https://github.com/json-api/json-api/tree/profile-extensions).
-There is no official support for extensions in the base JSON:API specification.
+Anyone can author a profile, and a single profile can be reused by multiple APIs.
+Popular profiles may be implemented by off-the-shelf tools so that developers
+can seamlessly take advantage of the features these profiles provide.
+
+## Existing Profiles
+
+{% for category in site.profile_categories %}
+
+ {{ category }}
+
+
+ {% for profile in site.profiles %}
+ {% if profile.categories contains category %}
+
+{% endfor %}
+
+## Creating a New Profile
+
+### Before Creating a Profile
+
+Please **check whether an existing profile fits your needs or could be amended
+to fit your needs** before developing a new one.
+
+- If a suitable profile already exists, consider using it. Having fewer, more
+widely-deployed profiles makes it easier to create shared tooling.
+
+- If there's an existing profile that could be amended to fit your needs,
+consider asking the profile's author if they would be willing to modify it as
+needed. Contact them through the information in their profile's registration
+and give them some time to reply.
+
+### Authoring Your Profile
+
+To author your profile, [download and fill out the template](/profile_template.md).
+
+### Register & Use Your Profile
+
+Once you've authored your profile, submit it to the JSON:API profile registry.
+By registering your profile:
+
+1. it will be listed above for others to find and reuse.
+
+2. it will be given an official url on jsonapi.org. This will be the URL you and
+ others use to apply/identify the profile.
+
+3. one of the JSON:API's editors will review your submission to check that it
+ follows the [profile extension requirements](/format/1.1/#profiles-authoring).
+ These requirements can be a bit tricky, so getting an expert review ensures
+ that your profile is legal for use with JSON:API.
+
+To register your profile:
+
+1. Choose a namespace, which is a name that uniquely identifies you or your
+ organization. (All profiles authored by the same person or organization are
+ registered under the same namespace, to prevent naming conflicts.) This
+ namespace should be your Github username, or the name of a Github organization
+ to which you belong. In unusual circumstances, you can request to use a
+ different name as the namespace in your PR (see below).
+
+2. Create a PR to [the json-api repository](https://github.com/json-api/json-api).
+ In the PR, make a directory at `_profiles/{NAMESPACE}/{PROFILE_NAME}` (where
+ `PROFILE_NAME` is the name of your profile, dasherized), and put your filled
+ out template as the `index.md` file in that directory folder. (See [an example](https://github.com/json-api/json-api/tree/1.1/_profiles/ethanresnick/cursor-pagination).)
+
+Once submitted, one of JSON:API's editors will review your profile to check that
+it: 1) follows the template above; 2) complies with JSON:API's [requirements for profiles](/format/1.1/#profiles-authoring);
+and 3) wouldn't cause any problems were it to become widely adopted. If your
+profile meets these three criteria, it will generally be **approved within a week**.
+
+In limited cases (e.g., if your profile defines a new, fundamental mechanism for
+doing something "architectural" that other profiles may need to do too), it might
+take longer for the reviewer to adequately check that the profile wouldn't have
+problematic ramifications if it became widely adopted.
+
+As part of this review, the editors or the community might also give design
+feedback on your submission. You can take as much time as you'd like to act on/
+respond to this feedback, and the editors will wait for you to say that your
+submission is finalized before merging your PR. You are encouraged, but not
+required, to act on any design feedback.
+
+If you do change your submission after it's been reviewed, it will be re-reviewed
+to make sure it still complies with the requirements for approval given above.
+
+JSON:API's editors may occasionally reassign responsibility for a registered
+profile. The most common case of this will be to enable changes to be made to
+profiles where the author of the registration has died, moved out of contact or
+is otherwise unable to make changes that are important to the community.
+
+Even though profile registration is strongly encouraged, it is not mandatory.
+If you choose not to register your profile, you can create your own URL, on
+a domain you control, and use that to identify your profile. You should
+self-host the filled out template at this URL.
## Prior Extensions
JSON:API previously offered experimental support for a different extension
-negotiation system than the one now being discussed, and it provided a number of
-extensions for use with that old negotiation system. However, this system was
-always experimental and has now been deprecated.
+negotiation system than the one now in the specification, and it provided a
+number of extensions for use with that old negotiation system. However, this
+system was always experimental and has now been deprecated.
-New APIs should not use the old system or any extensions designed for it. APIs
-that already use these old extensions should direct clients to an
+New APIs should not use the old system or any extensions designed for it.
+APIs that already use these old extensions should direct clients to an
[earlier version of this page](https://github.com/json-api/json-api/blob/9c7a03dbc37f80f6ca81b16d444c960e96dd7a57/extensions/index.md)
as documentation.
diff --git a/index.md b/index.md
index 61e4af60d..8443cd877 100644
--- a/index.md
+++ b/index.md
@@ -116,10 +116,12 @@ specification](/format).
## Extensions
-JSON:API has [experimental support for extensions](/extensions).
+The JSON:API community has created a collection of extensions that APIs can use
+to provide clients with information or functionality beyond that described in the
+base JSON:API specification. These extensions are called profiles.
-Official extensions are being developed for [Bulk](/extensions/bulk/) and
-[JSON Patch](/extensions/jsonpatch/) operations.
+You can [browse existing profiles](/extensions/#existing-profiles) or
+[create a new one](/extensions/#profile-creation).
## Update history
diff --git a/javascripts/all.js b/javascripts/all.js
index 4a4ca3277..d030f35af 100644
--- a/javascripts/all.js
+++ b/javascripts/all.js
@@ -1,6 +1,17 @@
//= require_tree .
$(document).ready(function() {
+ // Add links within headings from user-provided profiles markup for
+ // easy jumping and so outline generation works.
+ $('h2, h3, h4, h5', document.querySelector('#profile-spec-container')).each(function() {
+ var $this = $(this);
+
+ if($this.find("a.headerlink").length === 0 && $this.attr('id')) {
+ $this.prepend('');
+ }
+ });
+
+
// Build navigation list
var documentOutlineElement = $("#document-outline");
@@ -12,7 +23,6 @@ $(document).ready(function() {
// Sidebar scroll affix
fixElement($(".sidebar"), $("footer"), 52);
-
activateVersionPicker();
});
@@ -89,6 +99,14 @@ function createOutlineFromElement(element) {
children: []
};
+ $(this).nextUntil('h3', 'h4').each(function() {
+ childItem.children.push({
+ title: $(this).not('a').text(),
+ href: $(this).find('a').attr('href') || "#",
+ children: []
+ });
+ });
+
item.children.push(childItem);
});
@@ -98,10 +116,6 @@ function createOutlineFromElement(element) {
return outline;
}
-/**
- * Creates a nested list from an array in the form returned by `createOutlineFromElement`.
- */
-
/**
* Creates a nested list from an array in the form returned by `createOutlineFromElement`.
*
diff --git a/profile_template b/profile_template
new file mode 100644
index 000000000..610688975
--- /dev/null
+++ b/profile_template
@@ -0,0 +1,54 @@
+---
+permalink: /profile_template.md
+---
+---
+name: Your Profile's Name
+short_description: |
+ A short descriptions (1-3 sentences) about your profile's purpose and
+ features, to help others determine if it will work for them.
+
+extended_description: |
+ A description used in the introduction section on the profile's dedicated
+ page. This can be as long as you want, and can contain markdown.
+
+# If your profile defines values that can only be used in document members that
+# were introduced after JSON:API v1.0, change the minimum_jsonapi_version field
+# and fill in the minimum_jsonapi_version_explanation field with an explanation
+# of the features you're relying on from the JSON:API version you've indicated.
+minimum_jsonapi_version: 1.0
+minimum_jsonapi_version_explanation:
+
+# Url of a Github repo or some other place where people can
+# ask questions/start discussions about your extension.
+discussion_url: http://tets.com
+
+author_name: Ethan Resnick
+author_email: ethan.resnick@gmail.com
+# Optional fields
+author_website: https://ethanresnick.com/
+author_phone: +13104398032 # use `tel` url format.
+
+# Valid categories are listed under the profile_categories section
+# in https://github.com/json-api/json-api/blob/gh-pages/_config.yml
+categories:
+ - Filtering
+---
+
+Here, specify your profile. This description should be detailed enough to allow
+for interoperable implementations, but it otherwise doesn't need to be formal.
+At the very least, describe the elements (document data or query parameters)
+your profile defines and the allowed values for each.
+
+Make sure you __follow the authoring guidelines__, which describe what profiles
+are and are not allowed to do, and how they should be designed to evolve over time.
+
+Those authoring guidelines are at:
+ http://jsonapi.org/format/1.1/#profiles-authoring
+
+Finally, it is **strongly encouraged** that your profile reuse objects,
+patterns, and key names from the main JSON API specification where appropriate.
+
+For instance, if your profile defines a field that points to another JSON API
+resource, that field should hold a resource identifier object. Or, if your
+extension defines a "type" key, it should use that key as JSON API does
+(i.e. to hold a string indicating a resource's type).
diff --git a/stylesheets/all.css b/stylesheets/all.css
index 833eb002d..fab5a3766 100644
--- a/stylesheets/all.css
+++ b/stylesheets/all.css
@@ -93,7 +93,7 @@ footer .site-wrapper, footer .social-links {
text-transform: uppercase;
letter-spacing: 0.2px; }
-header h1, footer .social-links span {
+header h1#json-api, footer .social-links span {
text-indent: -119988px;
overflow: hidden;
text-align: left;
@@ -134,7 +134,7 @@ h1 {
color: #0b4e22; }
h2 {
- margin: 50px 0 15px 0;
+ margin: 2em 0 15px 0;
color: #5a5a5a;
font-size: 22px;
line-height: 1.3; }
@@ -165,13 +165,19 @@ th {
a {
color: #5a5a5a; }
-blockquote {
+blockquote, .note {
margin: 20px 0;
- padding: 10px 15px 0 15px;
+ padding: 10px 15px 10px 15px;
border-radius: 4px;
color: gray;
background: rgba(90, 90, 90, 0.04);
border: 1px solid #cccccc; }
+ blockquote code, .note code {
+ background: inherit !important; }
+ blockquote p, .note p {
+ margin-bottom: 6px; }
+ blockquote p:last-child, .note p:last-child {
+ margin-bottom: 0; }
p > code, li > code {
padding: 1px 4px;
@@ -181,6 +187,9 @@ p > code, li > code {
code {
font-weight: inherit; }
+address {
+ font-style: normal; }
+
/** Navigation */
.site-nav {
font-weight: 500;
@@ -226,7 +235,7 @@ header .content {
padding: 29px 20px 41px;
background: #ebebeb;
border-bottom: 1px solid #cccccc; }
-header h1 {
+header h1#json-api {
text-align: center;
height: 130px;
margin: 0;
@@ -304,21 +313,21 @@ footer {
margin-bottom: 50px; }
.sidebar {
- width: 220px;
+ width: 235px;
margin-top: 8px;
float: left; }
.sidebar h1 {
font-size: 15px;
background-color: white;
z-index: 1;
- width: 220px;
+ width: 235px;
color: inherit;
font-weight: 300; }
.sidebar nav > ol {
- width: 220px;
+ width: 235px;
overflow-y: auto; }
.sidebar #version-picker-wrapper {
- width: 220px;
+ width: 200px;
margin-bottom: 20px;
display: flex;
flex-direction: row; }
@@ -343,7 +352,7 @@ footer {
@media screen and (min-width: 886px) {
.sidebar + .content {
- margin-left: 235px;
+ margin-left: 245px;
padding-left: calc(100vw - 886px); } }
@media screen and (min-width: 901px) {
.sidebar + .content {
@@ -380,4 +389,27 @@ pre.highlight {
margin: 2em 0;
font-family: Menlo, monospace; }
+/** Extensions page */
+.profiles-list dt, .profiles-list dd {
+ display: inline;
+ margin-left: 0; }
+.profiles-list dd::after {
+ display: block;
+ content: " ";
+ height: 0.8em; }
+.profiles-list dt::before {
+ content: "•";
+ font-size: 1.56em;
+ margin-right: 1em;
+ position: absolute;
+ margin-left: -0.66em;
+ line-height: 1.1em; }
+
+/** Individual profile page */
+.profile-page h2 {
+ margin-top: 2em; }
+
+.profile-page h1 + section h2 {
+ margin-top: 1em; }
+
/*# sourceMappingURL=all.css.map */
diff --git a/stylesheets/all.css.map b/stylesheets/all.css.map
index 07354ac35..b414463a8 100644
--- a/stylesheets/all.css.map
+++ b/stylesheets/all.css.map
@@ -1,6 +1,6 @@
{
"version": 3,
-"mappings": "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",
+"mappings": "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",
"sources": ["highlight.scss","fontello.scss","all.sass","../../../../../../../usr/local/lib/ruby/gems/2.4.0/gems/compass-core-1.0.3/stylesheets/compass/typography/text/_replacement.scss","../../../../../../../usr/local/lib/ruby/gems/2.4.0/gems/compass-core-1.0.3/stylesheets/compass/css3/_flexbox.scss","../../../../../../../usr/local/lib/ruby/gems/2.4.0/gems/compass-core-1.0.3/stylesheets/compass/_support.scss","../../../../../../../usr/local/lib/ruby/gems/2.4.0/gems/compass-core-1.0.3/stylesheets/compass/utilities/general/_clearfix.scss","../../../../../../../usr/local/lib/ruby/gems/2.4.0/gems/compass-core-1.0.3/stylesheets/compass/utilities/general/_hacks.scss"],
"names": [],
"file": "all.css"
diff --git a/stylesheets/all.sass b/stylesheets/all.sass
index 1007217c0..aa3b62642 100644
--- a/stylesheets/all.sass
+++ b/stylesheets/all.sass
@@ -6,7 +6,7 @@
/** Config */
$page-width: 955px
-$sidebar-width: 220px
+$sidebar-width: 235px
$page-padding: 20px
$mobile-breakpoint: 800px
@@ -19,6 +19,7 @@ $color-masthead: rgb(235, 235, 235)
$fonts-body: "Helvetica Neue", "Helvetica", Arial, "Open Sans", sans-serif
$border-radius-main: 4px
+$h2-margin-top: 2em
/** Mixins */
@@ -83,7 +84,7 @@ h1
color: $color-accent
h2
- margin: 50px 0 15px 0
+ margin: $h2-margin-top 0 15px 0
color: $color-main
font-size: 22px
line-height: 1.3
@@ -115,14 +116,23 @@ th
a
color: $color-main
-blockquote
+blockquote, .note
margin: 20px 0
- padding: 10px 15px 0 15px
+ padding: 10px 15px 10px 15px
border-radius: $border-radius-main
color: lighten($color-main, 15%)
background: rgba($color-main, 0.04)
border: 1px solid $color-light-rule
+ code
+ background: inherit !important
+
+ p
+ margin-bottom: 6px
+
+ p:last-child
+ margin-bottom: 0
+
p > code, li > code
padding: 1px 4px
background: rgba(0, 0, 0, 0.06)
@@ -131,6 +141,9 @@ p > code, li > code
code
font-weight: inherit
+address
+ font-style: normal
+
/** Navigation */
.site-nav
@@ -190,7 +203,7 @@ header
background: $color-masthead
border-bottom: 1px solid $color-light-rule
- h1
+ h1#json-api
@extend %hide-text
text-align: center
height: 130px
@@ -307,7 +320,7 @@ footer
overflow-y: auto
#version-picker-wrapper
- width: $sidebar-width
+ width: 200px
margin-bottom: 20px
display: flex
flex-direction: row
@@ -347,7 +360,7 @@ footer
// just jump to its final size at the next media query.
@media screen and (min-width: $page-width + ($page-padding * 2) - 109px)
.sidebar + .content
- margin-left: $sidebar-width + 15px
+ margin-left: $sidebar-width + 10px
padding-left: calc(100vw - #{$page-width + ($page-padding * 2) - 109px})
@media screen and (min-width: $page-width + ($page-padding * 2) - 94px)
@@ -392,3 +405,33 @@ pre.highlight
line-height: 16px
margin: 2em 0
font-family: Menlo, monospace
+
+
+/** Extensions page */
+.profiles-list
+ dt, dd
+ display: inline
+ margin-left: 0
+
+ dd::after
+ display: block
+ content: " "
+ height: .8em
+
+ dt::before
+ content: "\2022"
+ font-size: 1.56em
+ margin-right: 1em
+ position: absolute
+ margin-left: -.66em
+ line-height: 1.1em
+
+/** Individual profile page */
+// Our profile pages have more sophisticated markup than our
+// main format pages, necessitating slightly different CSS.
+// This is a bit of a mess.. but so is this whole file.
+.profile-page h2
+ margin-top: $h2-margin-top
+
+.profile-page h1 + section h2
+ margin-top: 1em