Given the following:
GET http://www.example.com/post/1?include=author
{
"type": "post",
"id": "1",
"attributes": {
"title": "It's a title.",
"description": "It's a description."
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/people?filter[article]=1"
},
"data":{
"type":"people", "id":"9"
}
}
},
"included":[
{
"type":"people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}
],
"links": {
"self": "http://example.com/articles/1"
}
}
If I were to append fields[post]=title, (i.e. GET http://www.example.com/post/1?include=author&fields[post]=title) should this prevent included (compound document) from displaying?
GET http://www.example.com/post/1?include=author&fields[post]=title
{
"type": "post",
"id": "1",
"attributes": {
"title": "It's a title.",
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/people?filter[article]=1"
},
"data":{
"type":"people", "id":"9"
}
}
}
}
Or are compound documents still supposed to render?
Sparse Fieldsets and Compound Documents could be used together. The spec explicitly lists sparse fieldsets as the only allowed case in which a included resource may not be linked by another other resource in the same document:
** Compound Documents**
Compound documents require “full linkage”, meaning that every included resource MUST be identified by at least one resource identifier object in the same document. These resource identifier objects could either be primary data or represent resource linkage contained within primary or included resources.
The only exception to the full linkage requirement is when relationship fields that would otherwise contain linkage data are excluded via sparse fieldsets.
To directly answer your question: The spare fieldset should only affect the linkage between the resources (e.g. by not including the relationship field between them) but should not cause a resource to be not included that otherwise would.
Related
Is it a violation of the JSON-API spec to allow reverse relationships to be created automatically?
I need to create resources that, when I link A to B in a relationship, automatically links B to A. In this way I can traverse A to find all of its Bs and can find the parent A from a B. However, I don't want to POST/PATCH to 2 relationships to get this right. I want to establish the relationship once.
Now I know that it is an implementation detail as to how the server maintains link/references as well as how the behaviour is established but I want to build the API in such a way that it doesn't violate the spec.
Assuming I have resources Books and Authors. Books have Authors and Authors have Books. The question is, once I relate an Author to a Book, I need the reverse relationship to be created as well. Is it a violation of the spec in any way to assume that this reverse relationship can be automatically created by simply doing one POST to the Books resource's relationship?
By way of example, starting with the book.
{
"data": {
"type": "books", "id": 123, "attributes": ...,
"links": { "self": "/books/123" },
"relationships": {
"self": "/books/123/relationships/authors",
"related": "/books/123/authors"
}
}
}
And the author
{
"data": {
"type": "authors", "id": 456, "attributes": ...,
"links": { "self": "/authors/456" },
"relationships": {
"self": "/authors/456/relationships/books",
"related": "/authors/456/books"
}
}
}
If I establish the link from a book to an author with a POST to /books/123/relationships/authors
{
"data": [{ "data": "authors", "id": "456" }]
}
Do I need to explicitly do the same for the Author 456 as a POST to /authors/456/relationships/books?
{
"data": [{ "data": "books", "id": "123" }]
}
Or can I let the server build the relationship for me so that I can avoid the second POST and just see the automatic reverse relationship at GET /authors/456/relationships/books?
From the perspective of the spec this is only one relationship represented from two different sides. author and book have a many-to-many relationship. This relationship could be represented in author's resource object as well as book's resource object and of course also via there relationship links. Actually it would be a violation of the spirit of the specification if representations wouldn't match. Having one-sided relationships is another story but in that case one side wouldn't know about the relationships at all (e.g. a book is associated with an author but the author model does not know which books are associated with it).
A post to either one side of that relationship creates the relationship between the two records. It shouldn't matter which side is used to create that relationship and if it's created as part of a creation / update to a resource via it's resource object or via a relationship link representing that relationship. The same applies to deletion of that relationship.
Maybe an example would make that even more clear. Let's assume a book is created with a POST to /books?include=author having these payload:
{
"data": {
"type": "books",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "1"
}
}
}
}
}
The response may look like this:
{
"data": {
"type": "books",
"id": "7",
"relationships": {
"author": {
"data": { "type": "authors", "id": "1" }
}
}
},
"included": [
{
"type": "authors",
"id": "1",
"relationships": {
"books": {
"data": [
{ "type": "books", "id": "7" }
]
}
}
}
]
}
According to the spec, resource identifier objects do not hold attributes.
I want to do a POST to create a new resource which includes other nested resource.
These are the basic resources: club (with name) and many positions (type). Think a football club with positions like goalkeeper, goalkeeper, striker, striker, etc.
When I do this association, I want to set some attributes like is the position required for this particular team. For example I only need 1 goalkeeper but I want to have a team with many reserve goalkeepers. When I model these entities in the DB I'll set the required attribute in a linkage table.
This is not compliant with JSON API:
{
"data": {
"type": "club",
"attributes": {
"name": "Backyard Football Club"
},
"relationships": {
"positions": {
"data": [{
"id": "1",
"type": "position",
"attributes": {
"required": "true"
}
}, {
"id": "1",
"type": "position",
"attributes": {
"required": "false"
}
}
]
}
}
}
}
This is also not valid:
{
"data": {
"type": "club",
"attributes": {
"name": "Backyard Football Club",
"positions": [{
"position_id": "1",
"required": "true"
},
{
"position_id": "1",
"required": "false"
}]
}
}
}
So how is the best way to approach this association?
The best approach here will be to create a separate resource for club_position
Creating a club will return a url to a create club_positions, you will then post club_positions to that url with a relationship identifier to the position and club resource.
Added benefit to this is that club_positions creation can be parallelized.
Assuming we have the following data structure
"data": [
{
"type": "node--press",
"id": "f04eab99-9174-4d00-bbbe-cdf45056660e",
"attributes": {
"nid": 130,
"uuid": "f04eab99-9174-4d00-bbbe-cdf45056660e",
"title": "TITLE OF NODE",
"revision_translation_affected": true,
"path": {
"alias": "/press/title-of-node",
"pid": 428,
"langcode": "es"
}
...
}
The data returned is compliant with JSON API standards, and I have no problem retrieving and processing it, except for the fact that I need to be able to filter the nodes returned by the path pid.
How can I filter my data by path.pid?
I have tried:
- node-press?filter[path][pid]=428
- node-press?filter[path][pid][value]=428
to no avail
It's not well defined in the filters section of the specification but other parameters such as include describe accessing nested keys with dot-notation. You could try ?filter[path.pid]=428 and parse the filter that way.
"field_country": {
"data": {
"type": "taxonomy_term--country",
"id": "818f11ab-dd9d-406b-b1ca-f79491eedd73"
}
}
Above structure can be filtered by ?filter[field_country.id]=818f11ab-dd9d-406b-b1ca-f79491eedd73
Why is the self and related references different in the below JSONAPI resource? Aren't they pointing to the same resource? What is the difference between going to /articles/1/relationships/tags and /articles/1/tags?
{
"links": {
"self": "/articles/1/relationships/tags",
"related": "/articles/1/tags"
},
"data": [
{ "type": "tags", "id": "2" },
{ "type": "tags", "id": "3" }
]
}
You can read about that here: https://github.com/json-api/json-api/issues/508.
Basically, with /articles/1/relationships/tags response will be object which represents relationship between articles and tags. The response could be something like this (what you put in your question):
{
"links": {
"self": "/articles/1/relationships/tags",
"related": "/articles/1/tags"
},
"data": [
{ "type": "tags", "id": "2" },
{ "type": "tags", "id": "3" }
]
}
This response gives only the necessary data (in primary data attribute - data) to manipulate the relationship and not resources connected with relationship. That being said, you'll call /articles/1/relationships/tags if you want to create new relationship, add a new tag (basically updating relationship) to article, read which tags belong to article (you only need identity to search them on server) or delete article tags.
On the other hand, calling /articles/1/tags will respond with tags as primary data with all the other properties that they have (articles, relationships, links, and other top-level attributes such include, emphasized text, links and/or jsonapi).
They are different. Here is an example from my project.
Try Get http://localhost:3000/phone-numbers/1/relationships/contact you will get response like this:
{
"links": {
"self": "http://localhost:3000/phone-numbers/1/relationships/contact",
"related": "http://localhost:3000/phone-numbers/1/contact"
},
"data": {
"type": "contacts",
"id": "1"
}
}
You didn't get the attributes and relationships which is probably you want to retrieve.
Then
Try Get http://localhost:3000/phone-numbers/1/contact you will get response like this:
{
"data": {
"id": "1",
"type": "contacts",
"links": {
"self": "http://localhost:3000/contacts/1"
},
"attributes": {
"name-first": "John",
"name-last": "Doe",
"email": "john.doe#boring.test",
"twitter": null
},
"relationships": {
"phone-numbers": {
"links": {
"self": "http://localhost:3000/contacts/1/relationships/phone-numbers",
"related": "http://localhost:3000/contacts/1/phone-numbers"
}
}
}
}
}
You can see you retrieved all the information you want, including the attributes and relationships.
But you should know that relationships can be used for some purpose. Please read http://jsonapi.org/format/#crud-updating-to-one-relationships as a sample.
I'm thinking of using the JSONAPI standard for the design of our API. One thing this API must be able to do, is accept a compound document (several layers deep) and create it. The root object owns all descendants ('to-many' relationships) which the server knows nothing about at that point, so it's not possible for the client to provide an id.
Is this supported by the specification or does the client have to issue http requests for every object in the document in order?
from http://jsonapi.org/format/#document-compound-documents
Compound documents require "full linkage", meaning that every included
resource MUST be identified by at least one resource identifier object
in the same document. These resource identifier objects could either
be primary data or represent resource linkage contained within primary
or included resources. The only exception to the full linkage
requirement is when relationship fields that would otherwise contain
linkage data are excluded via sparse fieldsets.
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "http://example.com/articles/1"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}