Collections¶
A collection belongs to a bucket and stores records.
A collection is a mapping with the following attributes:
data
: (optional) attributes of the collection objectid
: the collection object idlast_modified
: the timestamp of the last modificationschema
: (optional) a JSON schema to validate the collection recordscache_expires
: (optional, in seconds) add client cache headers on read-only requests. More details...- and any field you might need
permissions
: the ACLs for the collection object
Note
When the built-in plugin kinto.plugins.default_bucket
is enabled in
configuration, a bucket default
is available.
Users are assigned to that bucket which can be used for their personal data.
When going through the default
bucket, the collections are created
silently upon first access.
Applications can use this default bucket (e.g. /buckets/default/collections/contacts
will be
the contacts of the current user.
Internally the user default bucket is assigned to an ID, and users can share data from their personnal bucket, by sharing its URL using the full ID.
List bucket collections¶
-
GET
/buckets/
(bucket_id)/collections
¶ Synopsis: List bucket’s readable collections Requires authentication
Example Request
$ http GET http://localhost:8888/v1/buckets/blog/collections --auth="token:bob-token" --verbose
GET /v1/buckets/blog/collections HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic YWxpY2U6 Connection: keep-alive Host: localhost:8888 User-Agent: HTTPie/0.9.2
HTTP/1.1 200 OK Access-Control-Expose-Headers: Content-Length, Expires, Alert, Retry-After, Last-Modified, Total-Records, ETag, Pragma, Cache-Control, Backoff, Next-Page Cache-Control: no-cache Content-Length: 144 Content-Type: application/json; charset=UTF-8 Date: Fri, 26 Feb 2016 14:14:40 GMT Etag: "1456496072475" Last-Modified: Fri, 26 Feb 2016 14:14:32 GMT Server: waitress Total-Records: 3 { "data": [ { "id": "scores", "last_modified": 1456496072475 }, { "id": "game", "last_modified": 1456496060675 }, { "id": "articles", "last_modified": 1456496056908 } ] }
A Total-Records
response header indicates the total number of objects
of the list (not the response, since it can be paginated).
A Last-Modified
response header provides a human-readable (rounded to second)
of the current collection timestamp.
For cache and concurrency control, an ETag
response header gives the
value that consumers can provide in subsequent requests using If-Match
and If-None-Match
headers (see section about timestamps).
List of available URL parameters¶
<prefix?><field name>
: filter by value(s)_since
,_before
: polling changes_sort
: order list_limit
: pagination max size_token
: pagination token_fields
: filter the fields of the records
Filtering, sorting, partial responses and paginating can all be combined together.
?_sort=-last_modified&_limit=100&_fields=title
HTTP Status Codes¶
200 OK
: The request was processed304 Not Modified
: List has not changed since value inIf-None-Match
header400 Bad Request
: The request querystring is invalid401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible406 Not Acceptable
: The client doesn’t accept supported responses Content-Type412 Precondition Failed
: List has changed since value inIf-Match
header
Delete bucket collections¶
-
DELETE
/buckets/
(bucket_id)/collections
¶ Synopsis: Delete every writable collections in this bucket Requires authentication
Example Request
$ http delete http://localhost:8888/v1/buckets/blog/collections --auth="token:bob-token" --verbose
DELETE /v1/buckets/blog/collections HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic YWxpY2U6 Connection: keep-alive Content-Length: 0 Host: localhost:8888 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff Content-Length: 189 Content-Type: application/json; charset=UTF-8 Date: Fri, 26 Feb 2016 14:19:21 GMT Server: waitress { "data": [ { "deleted": true, "id": "articles", "last_modified": 1456496361303 }, { "deleted": true, "id": "game", "last_modified": 1456496361304 }, { "deleted": true, "id": "scores", "last_modified": 1456496361305 } ] }
It supports the same filtering, sorting and pagination capabilities as GET.
If the number of records to be deleted exceeds to pagination limit, a response
header Next-Page
will be provided.
If the If-Match: "<timestamp>"
request header is provided, and if the list
has changed meanwhile, a 412 Precondition Failed
error is returned.
HTTP Status Codes¶
200 OK
: The objects were deleted401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible405 Method Not Allowed
: This endpoint is not available406 Not Acceptable
: The client doesn’t accept supported responses Content-Type412 Precondition Failed
: The list has changed since value inIf-Match
header
Creating a collection¶
-
POST
/buckets/
(bucket_id)/collections
¶ Synopsis: Creates a new collection. If id
is not provided, it is automatically generated.Requires authentication
Example Request
$ echo '{"data": {"id": "articles"}}' | http POST http://localhost:8888/v1/buckets/blog/collections --auth="token:bob-token" --verbose
POST /v1/buckets/blog/collections HTTP/1.1 Accept: application/json Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Content-Length: 29 Content-Type: application/json Host: 127.0.0.1:8888 User-Agent: HTTPie/0.9.2 { "data": { "id": "articles" } }
HTTP/1.1 201 Created Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff Content-Length: 159 Content-Type: application/json; charset=UTF-8 Date: Thu, 21 Jan 2016 00:41:25 GMT Server: waitress { "data": { "id": "articles", "last_modified": 1453336885287 }, "permissions": { "write": [ "basicauth:797df8e4abfb8426cccaeba3b69109c9e8d09fcdfe264d5bba1eb2a239bcf832" ] } }
If the If-Match: "<timestamp>"
request header is provided as described in
the section about timestamps, and if the list has
changed meanwhile, a 412 Precondition Failed
error is returned.
If the If-None-Match: *
request header is provided, and if the provided data
contains an id
field, and if there is already an existing object with this id
,
a 412 Precondition Failed
error is returned.
Important
If the posted object has an id
field, it will be taken into account.
However, if a object already exists with the same id
, a 200 OK
response
is returned with the existing object in body (instead of 201 Created
).
See https://github.com/Kinto/kinto/issues/140
Validation¶
If the posted values are invalid (e.g. field value is not an integer)
an error response is returned with 400 Bad Request
.
Permissions¶
In the JSON request payloads, an optional permissions
attribute can be provided.
The current user id is always added among the write
principals.
HTTP Status Codes¶
200 OK
: This object already exists, the one stored on the database is returned201 Created
: The object was created400 Bad Request
: The request body is invalid401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible406 Not Acceptable
: The client doesn’t accept supported responses Content-Type412 Precondition Failed
: List has changed since value inIf-Match
header415 Unsupported Media Type
: The client request was not sent with a correct Content-Type
Replacing a collection¶
-
PUT
/buckets/
(bucket_id)/collections/
(collection_id)¶ Synopsis: Creates or replaces a collection object. Requires authentication
A collection is the parent object of records. It can be viewed as a container where records permissions are assigned globally.
Example Request
$ http put http://localhost:8888/v1/buckets/blog/collections/articles --auth="token:bob-token" --verbose
PUT /v1/buckets/blog/collections/articles HTTP/1.1 Accept: application/json Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Content-Length: 0 Host: localhost:8888 User-Agent: HTTPie/0.9.2
HTTP/1.1 201 Created Access-Control-Expose-Headers: Backoff, Retry-After, Alert Content-Length: 159 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 15:36:34 GMT Server: waitress { "data": { "id": "articles", "last_modified": 1434641794149 }, "permissions": { "write": [ "basicauth:206691a25679e4e1135f16aa77ebcf211c767393c4306cfffe6cc228ac0886b6" ] } }
Validation and conflicts behaviour is similar to creating objects (POST
).
If the If-Match: "<timestamp>"
request header is provided as described in
the section about timestamps, and if the object has
changed meanwhile, a 412 Precondition Failed
error is returned.
If the If-None-Match: *
request header is provided and if there is already
an existing object with this id
, a 412 Precondition Failed
error is returned.
Permissions¶
In the JSON request payloads, at least one of data
and permissions
must be provided. Permissions can thus be replaced independently from data.
In the case of creation, if only permissions
is provided, an empty object is created.
The current user id is always added among the write
principals.
HTTP Status Code¶
201 Created
: The object was created200 OK
: The object was replaced400 Bad Request
: The request body is invalid401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible406 Not Acceptable
: The client doesn’t accept supported responses Content-Type.412 Precondition Failed
: Record was changed or deleted since value inIf-Match
header.415 Unsupported Media Type
: The client request was not sent with a correct Content-Type.
Updating a collection¶
-
PATCH
/buckets/
(bucket_id)/collections/
(collection_id)¶ Synopsis: Updates a collection object. Requires authentication
A collection is the parent object of records. It can be viewed as a container where records permissions are assigned globally.
Example Request
$ echo '{"data": {"fingerprint": "9cae1b2d0f2b7d09bcf5c1bf51544274"}}' | http patch http://localhost:8888/v1/buckets/blog/collections/articles --auth="token:bob-token" --verbose
PATCH /v1/buckets/blog/collections/articles HTTP/1.1 Accept: application/json Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Content-Length: 62 Content-Type: application/json Host: localhost:8888 User-Agent: HTTPie/0.9.2 { "data": { "fingerprint": "9cae1b2d0f2b7d09bcf5c1bf51544274" } }
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert Content-Length: 208 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 15:36:34 GMT Server: waitress { "data": { "id": "articles", "last_modified": 1434641794149, "fingerprint": "9cae1b2d0f2b7d09bcf5c1bf51544274" }, "permissions": { "write": [ "basicauth:206691a25679e4e1135f16aa77ebcf211c767393c4306cfffe6cc228ac0886b6" ] } }
If the object is missing (or already deleted), a 404 Not Found
is returned only
if the user has write access to the object parent, otherwise a 403 Forbidden
is returned to avoid leaking information about non-accessible objects.
The consumer might decide to ignore it.
If the If-Match: "<timestamp>"
request header is provided as described in
the section about timestamps, and if the object has
changed meanwhile, a 412 Precondition Failed
error is returned.
Note
last_modified
is updated to the current server timestamp, only if a
field value was changed.
Attributes merge¶
The provided values are merged with the existing object. For example:
{"a":"b"}
+{"a":"c"}
→{"a":"c"}
{"a":"b"}
+{"b":"c"}
→{"a":"b", "b":"c"}
{"a":"b"}
+{"a":null}
→{"a":null}
: attributes can’t be removed with patch{"a": {"b":"c"}}
+{"a":{"d":"e"}}
→{"a":{"d":"e"}}
: sub-objects are replaced, not merged
JSON merge
is currently supported using Content-Type: application/merge-patch+json
. This provides
support to merging sub-objects and removing attibutes. For example:
{"a":"b"}
+{"a":null}
→{}
{"a": {"b":"c"}}
+{"a":{"d":"e"}}
→{"a":{"b:c", "d":"e"}}
{}
+{"a":{"b":{"c":null}}}
→{"a":{"b":{}}}
Light response body¶
If a Response-Behavior
request header is set to light
,
only the fields whose value was changed are returned. If set to
diff
, only the fields whose value became different than
the one provided are returned.
Permissions¶
In the JSON request payloads, at least one of data
and permissions
must be provided. Permissions can thus be modified independently from data.
The current user id is always added among the write
principals.
JSON Patch Operations¶
JSON-Patch is a way to define a sequence of operations to be applied on a JSON object.
It’s possible to use JSON-Patch by sending the request header Content-Type: application/json-patch+json
.
When using this request header, the body should contain a list of operations, for example:
[
{ "op": "test", "path": "data/a", "value": "foo" },
{ "op": "remove", "path": "/data/a" },
{ "op": "add", "path": "/data/b", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/data/b", "value": 42 },
{ "op": "move", "from": "/data/a", "path": "/data/c" },
{ "op": "copy", "from": "/data/b", "path": "/data/d" }
]
For more information about each operation, please refer to JSON-Patch Specification.
This is very useful when altering permissions since there is no need to get the current value before adding some principal to the list. Value is not used on permission operations and can be omitted.
[
{ "op": "test", "path": "/permissions/read/fxa:alice" },
{ "op": "add", "path": "/permissions/read/system.Everyone" },
{ "op": "remove", "path": "/permissions/read/fxa:bob" }
]
HTTP Status Codes¶
200 OK
: The object was modified400 Bad Request
: The request body is invalid, or a read-only field was modified401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible404 Not Found
: The object does not exist or was deleted406 Not Acceptable
: The client doesn’t accept supported responses Content-Type.412 Precondition Failed
: Record changed since value inIf-Match
header415 Unsupported Media Type
: The client request was not sent with a correct Content-Type.
Retrieving an existing collection¶
-
GET
/buckets/
(bucket_id)/collections/
(collection_id)¶ Synopsis: Returns the collection object. Requires authentication
Example Request
$ http get http://localhost:8888/v1/buckets/blog/collections/articles --auth="token:bob-token" --verbose
GET /v1/buckets/blog/collections/articles HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Host: localhost:8888 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Last-Modified, ETag Content-Length: 159 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 15:52:31 GMT Etag: "1434642751314" Last-Modified: Thu, 18 Jun 2015 15:52:31 GMT Server: waitress { "data": { "id": "articles", "last_modified": 1434641794149 }, "permissions": { "write": [ "basicauth:206691a25679e4e1135f16aa77ebcf211c767393c4306cfffe6cc228ac0886b6" ] } }
If the If-None-Match: "<timestamp>"
request header is provided, and
if the object has not changed meanwhile, a 304 Not Modified
is returned.
HTTP Status Codes¶
200 OK
: The request was processed304 Not Modified
: Object did not change since value inIf-None-Match
header400 Bad Request
: The request header is invalid401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible404 Not Found
: The object does not exist or was deleted406 Not Acceptable
: The client doesn’t accept supported responses Content-Type412 Precondition Failed
: Object changed since value inIf-Match
header
Deleting a collection¶
-
DELETE
/buckets/
(bucket_id)/collections/
(collection_id)¶ Synopsis: Deletes a specific collection and everything under it. Requires authentication
Example Request
$ http delete http://localhost:8888/v1/buckets/blog/collections/articles --auth="token:bob-token" --verbose
DELETE /v1/buckets/blog/collections/articles HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Content-Length: 0 Host: localhost:8888 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert Content-Length: 71 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 15:54:02 GMT Server: waitress { "data": { "deleted": true, "id": "articles", "last_modified": 1434642842010 } }
If the object is missing (or already deleted), a 404 Not Found
is returned only
if the user has write access to the object parent, otherwise a 403 Forbidden
is returned to avoid leaking information about non-accessible objects.
The consumer might decide to ignore it.
If the If-Match
request header is provided, and if the object has
changed meanwhile, a 412 Precondition Failed
error is returned.
Note
Once deleted, an object will appear in the list when polling for changes,
with a deleted status (delete=true
) and will have most of its fields empty.
HTTP Status Codes¶
200 OK
: The object was deleted401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible404 Not Found
: The object does not exist or was already deleted406 Not Acceptable
: The client doesn’t accept supported responses Content-Type.412 Precondition Failed
: Record changed since value inIf-Match
header
Collection JSON schema¶
Requires setting kinto.experimental_collection_schema_validation
to True
.
A JSON schema can optionally be associated to a collection.
Once a schema is set, records will be validated during creation or update.
If the validation fails, a 400 Bad Request
error response will be
returned.
Note
JSON schema is quite verbose and not an ideal solution for every use-case. However it is universal and supported by many programming languages and environments.
Set or replace a schema¶
Just modify the schema
attribute of the collection object:
Example request
$ echo '{
"data": {
"schema": {
"title": "Blog post schema",
"type": "object",
"properties": {
"title": {"type": "string"},
"body": {"type": "string"}
},
"required": ["title"]
}
}
}' | http PATCH "http://localhost:8888/v1/buckets/blog/collections/articles" --auth token:admin-token --verbose
PATCH /v1/buckets/blog/collections/articles HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Authorization: Basic YWRtaW46
Connection: keep-alive
Content-Length: 236
Content-Type: application/json; charset=utf-8
Host: localhost:8888
User-Agent: HTTPie/0.8.0
{
"data": {
"schema": {
"properties": {
"body": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"title"
],
"title": "Blog post schema",
"type": "object"
}
}
}
Example response
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Content-Length
Content-Length: 300
Content-Type: application/json; charset=UTF-8
Date: Fri, 21 Aug 2015 12:31:40 GMT
Etag: "1440160300818"
Last-Modified: Fri, 21 Aug 2015 12:31:40 GMT
Server: waitress
{
"data": {
"id": "articles",
"last_modified": 1440160300818,
"schema": {
"properties": {
"body": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"title"
],
"title": "Blog post schema",
"type": "object"
}
},
"permissions": {
"write": [
"basicauth:780f1ecd9f57b01bef79608b45916d3bddd17f83461ac6240402e0ffff3596c5"
]
}
}
Records validation¶
Once a schema has been defined, the posted records must match it:
$ echo '{"data": {
"body": "Fails if no title"
}}' | http POST http://localhost:8888/v1/buckets/blog/collections/articles/records --auth "token:admin-token"
HTTP/1.1 400 Bad Request
Access-Control-Expose-Headers: Backoff, Retry-After, Alert
Content-Length: 192
Content-Type: application/json; charset=UTF-8
Date: Wed, 10 Jun 2015 10:17:01 GMT
Server: waitress
{
"code": 400,
"details": [
{
"description": "'title' is a required property",
"location": "body",
"name": "title"
}
],
"errno": 107,
"error": "Invalid parameters",
"message": "'title' is a required property"
}
Schema migrations¶
Kinto does not take care of schema migrations. But it gives the basics for clients to manage it.
If the validation succeeds, the record will receive a schema
field with the
schema version (i.e. the collection current last_modified
timestamp).
It becomes possible to use this schema
field as a filter on the collection
records endpoint in order to obtain the records that were not validated against a particular
version of the schema.
For example, GET /buckets/blog/collections/articles/records?min_schema=123456
.
Remove a schema¶
In order to remove the schema of a collection, just modify the schema
field
to an empty mapping.
Example request
echo '{"data": {"schema": {}} }' | http PATCH "http://localhost:8888/v1/buckets/blog/collections/articles" --auth token:admin-token --verbose
PATCH /v1/buckets/blog/collections/articles HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Authorization: Basic YWRtaW46
Connection: keep-alive
Content-Length: 26
Content-Type: application/json; charset=utf-8
Host: localhost:8888
User-Agent: HTTPie/0.8.0
{
"data": {
"schema": {}
}
}
Example response
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Content-Length
Content-Length: 171
Content-Type: application/json; charset=UTF-8
Date: Fri, 21 Aug 2015 12:27:04 GMT
Etag: "1440159981842"
Last-Modified: Fri, 21 Aug 2015 12:26:21 GMT
Server: waitress
{
"data": {
"id": "articles",
"last_modified": 1440159981842,
"schema": {}
},
"permissions": {
"write": [
"basicauth:780f1ecd9f57b01bef79608b45916d3bddd17f83461ac6240402e0ffff3596c5"
]
}
}
Collection caching¶
With the cache_expires
attribute on a collection, it is possible to add client
cache control response headers for read-only requests.
The client (or cache server or proxy) will use them to cache the collection
records for a certain amount of time, in seconds.
For example, set it to 3600
(1 hour):
echo '{"data": {"cache_expires": 3600} }' | http PATCH "http://localhost:8888/v1/buckets/blog/collections/articles" --auth token:admin-token
From now on, the cache control headers are set for the GET requests:
http "http://localhost:8888/v1/buckets/blog/collections/articles/records" --auth token:admin-token
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Content-Length, Next-Page, Total-Records, Last-Modified, ETag, Cache-Control, Expires, Pragma
Cache-Control: max-age=3600
Content-Length: 11
Content-Type: application/json; charset=UTF-8
Date: Mon, 14 Sep 2015 13:51:47 GMT
Etag: "1442238450779"
Expires: Mon, 14 Sep 2015 14:51:47 GMT
Last-Modified: Mon, 14 Sep 2015 13:47:30 GMT
Server: waitress
Total-Records: 0
{
"data": [{}]
}
If set to 0
, the collection records become explicitly uncacheable (no-cache
).
echo '{"data": {"cache_expires": 0} }' | http PATCH "http://localhost:8888/v1/buckets/blog/collections/articles" --auth token:admin-token
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Content-Length, Next-Page, Total-Records, Last-Modified, ETag, Cache-Control, Expires, Pragma
Cache-Control: max-age=0, must-revalidate, no-cache, no-store
Content-Length: 11
Content-Type: application/json; charset=UTF-8
Date: Mon, 14 Sep 2015 13:54:51 GMT
Etag: "1442238450779"
Expires: Mon, 14 Sep 2015 13:54:51 GMT
Last-Modified: Mon, 14 Sep 2015 13:47:30 GMT
Pragma: no-cache
Server: waitress
Total-Records: 0
{
"data": []
}
Note
This can also be forced from settings, see configuration section.