Consuming the API from the Client End¶
GET-ing Resources¶
A Collection¶
$ http GET http://localhost:6543/api/posts
{
"data": [
{
"type": "posts",
"id": "1",
"attributes": {
"content": "something insightful",
"published_at": "2015-01-01T00:00:00",
"title": "post1: alice.main"
},
"links": {
"self": "http://localhost:6543/api/posts/1"
},
"relationships": {
"author": {
"data": {
"id": "1",
"type": "people"
},
"links": {
"related": "http://localhost:6543/api/posts/1/author",
"self": "http://localhost:6543/api/posts/1/relationships/author"
},
"meta": {
"direction": "MANYTOONE",
"results": {}
}
},
"blog": {
"data": {
"id": "1",
"type": "blogs"
},
"links": {
"related": "http://localhost:6543/api/posts/1/blog",
"self": "http://localhost:6543/api/posts/1/relationships/blog"
},
"meta": {
"direction": "MANYTOONE",
"results": {}
}
},
"comments": {
"data": [],
"links": {
"related": "http://localhost:6543/api/posts/1/comments",
"self": "http://localhost:6543/api/posts/1/relationships/comments"
},
"meta": {
"direction": "ONETOMANY",
"results": {
"available": 0,
"limit": 10,
"returned": 0
}
}
}
}
},
"... 5 more results ..."
],
"links": {
"first": "http://localhost:6543/api/posts?sort=id&page%5Boffset%5D=0",
"last": "http://localhost:6543/api/posts?sort=id&page%5Boffset%5D=0",
"self": "http://localhost:6543/api/posts"
},
"meta": {
"results": {
"available": 6,
"limit": 10,
"offset": 0,
"returned": 6
}
}
}
Note that we have:
data
which is an array of posts objects, each with:a
type
, which is the collection namean
id
, which is the value of the primary key column (which may or may not be calledid
)attributes
, as expecteda
links
object with:a
self
link
relationship objects for each relationship with:
data
with resource identifiers for related objectsself
andrelated
linkssome other information about the relationship in
meta
links
with:self
andpagination
links
meta
with:some extra information about the number of results returned.
A Single Resource¶
$ http GET http://localhost:6543/api/posts/1
Returns a single resource object in data
and no pagination links.
{
"data": {
"type": "posts",
"id": "1",
"attributes": {
"content": "something insightful",
"published_at": "2015-01-01T00:00:00",
"title": "post1: alice.main"
},
"links": {
"self": "http://localhost:6543/api/posts/1"
},
"relationships": {
"author": {
"data": {
"id": "1",
"type": "people"
},
"links": {
"related": "http://localhost:6543/api/posts/1/author",
"self": "http://localhost:6543/api/posts/1/relationships/author"
},
"meta": {
"direction": "MANYTOONE",
"results": {}
}
},
"blog": {
"data": {
"id": "1",
"type": "blogs"
},
"links": {
"related": "http://localhost:6543/api/posts/1/blog",
"self": "http://localhost:6543/api/posts/1/relationships/blog"
},
"meta": {
"direction": "MANYTOONE",
"results": {}
}
},
"comments": {
"data": [],
"links": {
"related": "http://localhost:6543/api/posts/1/comments",
"self": "http://localhost:6543/api/posts/1/relationships/comments"
},
"meta": {
"direction": "ONETOMANY",
"results": {
"available": 0,
"limit": 10,
"returned": 0
}
}
}
}
},
"links": {
"self": "http://localhost:6543/api/posts/1"
},
"meta": {}
}
Sparse Fieldsets¶
We can ask only for certain fields (attributes and relationships are collectively known as fields).
Use the fields
parameter, parameterized by collection name
(fields[collection]), with the value set as a comma separated list of field
names.
So, to return only the title attribute and author relationship of each post:
$ http GET http://localhost:6543/api/posts?fields[posts]=title,author
The resulting json has a data
element with a list of objects something like
this:
{
"attributes": {
"title": "post1: bob.second"
},
"id": "6",
"links": {
"self": "http://localhost:6543/api/posts/6"
},
"relationships": {
"author": {
"data": {
"id": "2",
"type": "people"
},
"links": {
"related": "http://localhost:6543/api/posts/6/author",
"self": "http://localhost:6543/api/posts/6/relationships/author"
},
"meta": {
"direction": "MANYTOONE",
"results": {}
}
}
},
"type": "posts"
}
Sorting¶
You can specify a sorting attribute and order with the sort query parameter.
Sort posts by title:
$ http GET http://localhost:6543/api/posts?sort=title
and in reverse:
$ http GET http://localhost:6543/api/posts?sort=-title
Sorting by multiple attributes (e.g. sort=title,content
) and sorting by attributes of related objects (sort=author.name) are supported.
A sort on id is assumed unless the sort parameter is specified.
Pagination¶
You can specify the pagination limit and offset:
$ http GET http://localhost:6543/api/posts?fields[posts]=title\&page[limit]=2\&page[offset]=2
We asked for only the title
field above so that the results would be more
compact…
{
"data": [
{
"attributes": {
"title": "post1: alice.second"
},
"id": "3",
"links": {
"self": "http://localhost:6543/api/posts/3"
},
"relationships": {},
"type": "posts"
},
{
"attributes": {
"title": "post1: bob.main"
},
"id": "4",
"links": {
"self": "http://localhost:6543/api/posts/4"
},
"relationships": {},
"type": "posts"
}
],
"links": {
"first": "http://localhost:6543/api/posts?page%5Blimit%5D=2&sort=id&page%5Boffset%5D=0",
"last": "http://localhost:6543/api/posts?page%5Blimit%5D=2&sort=id&page%5Boffset%5D=4",
"next": "http://localhost:6543/api/posts?page%5Blimit%5D=2&sort=id&page%5Boffset%5D=4",
"prev": "http://localhost:6543/api/posts?page%5Blimit%5D=2&sort=id&page%5Boffset%5D=0",
"self": "http://localhost:6543/api/posts?fields[posts]=title&page[limit]=2&page[offset]=2"
},
"meta": {
"results": {
"available": 6,
"limit": 2,
"offset": 2,
"returned": 2
}
}
}
There’s a default page limit which is used if the limit is not specified and a maximum limit that the server will allow. Both of these can be set in the ini file.
Filtering¶
The JSON API spec doesn’t say much about filtering syntax, other than that it
should use the parameter key filter
. There are multiple filtering syntaxes
available in pyramid_jsonapi.
The first is simple filtering and has been available since the first release. It uses the following syntax:
filter[<attribute_spec>:<operator>]=<value>
where:
attribute_spec
is either a direct attribute name or a dotted path to an attribute via relationships (only one level of relationships is currently supported).operator
is one of the list of supported operators (Search (Filter) Operators).value
is the value to match on.
This is simple and reasonably effective. It’s a little awkward on readability though. If you feel that you have a syntax that is more readable, more powerful, easier to parse or has some other advantage, let me know - I’d be interested in any thoughts.
Search operators in sqlalchemy (called column comparators) must be registered before they are treated as valid for use in json-api filters. The procedure for registering them, and the list of those registered by default can be found in Search (Filter) Operators.
To specify another search filter syntax use the syntax name with a *
in
front in the square brackets after filter
, like
filter[*rql]=some rql filter
.
Filter languages available:
RQL defined here https://github.com/persvr/rql as implemented in rqlalchemy https://github.com/pjwerneck/rqlalchemy.
Filter Examples¶
Simple:
Find all the people with name ‘alice’:
http GET http://localhost:6543/api/people?filter[name:eq]=alice
Find all the posts published after 2015-01-03:
http GET http://localhost:6543/api/posts?filter[published_at:gt]=2015-01-03
Find all the posts with ‘bob’ somewhere in the title:
http GET http://localhost:6543/api/posts?filter[title:like]=*bob*
Find all the posts where the author has the name ‘alice’:
http GET http://localhost:6543/api/posts?filter[author.name:eq]=alice
RQL
Find all the people with name ‘alice’:
http :6543/api/people filter[*rql]='eq(name,alice)'
Find all the posts where the author has the name ‘alice’:
http :6543/api/posts filter[*rql]='eq((author,name),alice)'