Core Concepts
JSON:API has many concepts in the specification, not all of which are documented here. However, users of the module do not need to completely understand all of the concepts of the specification to become productive with this module. If you would like to go deeper into how JSON:API's documents are structured, why the module does something in one way or another, or just would like to learn more about the module's design, readers are encouraged to read the specification on jsonapi.org.
Document Structure
The JSON:API is highly opinionated about how JSON documents should be structured and what information must go into every request and/or response body.
Every request/response body must be underneath a single JSON object.
{
// your data here...
}
The data, or the information specific to a resource or resources, must live within this top-level object under the data
"member". A "member" is just a predefined key in the JSON object. The data
member can be either an object ({}
) or an array ([]
). When creating or updating a resource, this will always be a single object ({}
) representing a single item. Only when retrieving a "collection" of multiple resource will this property be an array.
{
"data": {
// Your resource data goes here.
}
}
Other top-level members include: errors
, meta
, links
, and included
. Of these, included
will be used most often, but will be addressed later in the documentation.
For more information on the top-level structure, you may consult the specification.
Within the data
and included
members are "resource objects" or "resource identifier objects". "Resource objects" represent the content of the resources (entities) with which you will be concerned. "Resource identifier objects" are like foreign keys in a database; they identify a resource without containing any of that resource's fields. In Drupal terms, a resource object is generally the JSON representation of a single entity, that may be a single node, a single taxonomy term, or a single user. In Drupal terms again, a resource identifier is just enough information to load an entity—you have its type and its ID and nothing else.
Every resource object must contain two members: type
and id
. The only exception to this in when creating a new entity, in this case, the id
may be omitted in order to allow Drupal to generate an id for the new resource. However, it is entirely possible for the client application to provide a UUID for the resource when creating a new entity. All ID's in the JSON:API are UUIDs.
The type
member is always required. The value for the type member is derived from the entity type name and bundle, where applicable. The type for an entity resource always follows the pattern entity_type--bundle
. As an example, the core article and basic page node types will be represented as: node--article
and node--page
.
Thus, on an entity with no required properties or fields, one can create a new entity with the following JSON:
{
"data": {
"type": "node--my-bundle",
}
}
This wouldn't be very useful though. We need to include actual values for the entity. To do this, JSON:API has two members for holding values, attributes
and relationships
. attributes
store values specific to the underlying resource. relationships
are values that belong to another resource in the system. In Drupal terms, relationships
usually represent values that are stored by an entity reference. On Drupal's core article bundle, this might be the uid
property. This is because the uid
property is an entity reference to the user that authored the article. The body of a document with attributes
and relationships
might look something like this:
{
"data": {
"type": "node--my-bundle",
"id": "2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e",
"attributes": {
"title": "An Example"
},
"relationships": {
"uid": {
"data": {
"type": "user--user",
"id": "53bb14cc-544a-4cf2-88e8-e9cdd0b6948f"
}
}
}
}
}
As you can see, the uid
property lives under the relationships
member. Like the main resource, it also contains a type
and an id
member since it is a separate and distinct resource.
Note that uid
does not have any attributes
or relationships
. This is because JSON:API will not include the contents of a relationship unless specifically asked to via by the use of a special query parameter, include
. More on that later in the documentation (See "Fetching resources (GET)").
For more details on the structure of resource objects, you may consult the specification.
§ "Virtual" Resource Identifiers
In some circumstances, Drupal permits a relationship to target a resource (an entity reference to target an entity) which is not stored in the database and is therefore not retrievable via JSON:API. The "virtual" resource identifier may signal different circumstances depending on its context, although it will always correspond to a resource which cannot be found.
Usage and meaning of the 'virtual' resource identifier in Drupal core
The taxonomy term parent
field is the most noteworthy example of this special case in Drupal core. This relationship field may contain a resource identifier for a "virtual" taxonomy term resource. In this case, the "virtual" resource identifier identifies the <root>
taxonomy term. As such, this signals that the referencing term is at the top level of its vocabulary.
Take the following response document for a hypothetical taxonomy term as an example:
{
"data": {
"type": "taxonomy_term--tags",
"id": "2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e",
"attributes": {
"name": "Politics"
},
"relationships": {
"parent": {
"data": [
{
"id": "virtual",
"type": "taxonomy_term--tags",
"meta": {
"links": {
"help": {
"href": "https://www.drupal.org/docs/8/modules/json-api/core-concepts#virtual",
"meta": {
"about": "Usage and meaning of the 'virtual' resource identifier."
}
}
}
}
}
]
}
}
}
}
Note how this Term
's parent
relationship (an entity reference field) has a resource identifier object where id
is not a UUID, it is "virtual"
. This is necessary because a top-level or root-level Term
has a reference to an unstored <root>
term (target_id = 0)
as its parent.
Why?
Given that the root term is not stored and that a Term
may have more than one parent, the crucial question to ask is: how do we distinguish between a Term
that:
- has only
Term
3
as its parent ([3]
) ? - has both this unstored root
Term
as its parent and aTerm
3
([0, 3]
)?
The answer is that if JSON:API were to omit the unstored root term 0
rather than use the "virtual"
ID, then it would not be possible to distinguish between those two cases!
§ "Missing" Resource Identifiers
Drupal does not "clean up" relationships to resources that have been deleted (entity reference fields that have references to entities that have been deleted). In other words: Drupal leaves "dangling" relationships (entity references) in place.
When JSON:API encounters such dangling relationships, it'll use the "missing" resource identifier.
Usage and meaning of the 'missing' resource identifier in Drupal core
Staying with the example given for the 'virtual' resource identifier: the taxonomy term parent
field. Imagine a particular taxonomy term used to have the "Belgium" taxonomy term as its parent, but now the "Belgium" taxonomy term resource no longer exists — perhaps because the small country of Belgium ceased to exist. Then this relationship field would contain a resource identifier for a "missing" taxonomy term resource.
Take the following response document for a hypothetical taxonomy term as an example:
{
"data": {
"type": "taxonomy_term--tags",
"id": "2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e",
"attributes": {
"name": "Politics"
},
"relationships": {
"parent": {
"data": [
{
"id": "missing",
"type": "unknown",
"meta": {
"links": {
"help": {
"href": "https://www.drupal.org/docs/8/modules/json-api/core-concepts#missing",
"meta": {
"about": "Usage and meaning of the 'missing' resource identifier."
}
}
}
}
}
]
}
}
}
}
Note how this Term
's parent
relationship (an entity reference field) has a resource identifier object where id
is not a UUID, it is "missing"
. Not only that, but its type
is unknown
(because Drupal does not store the bundle of the referenced entity, only the entity type, hence determining the JSON:API resource type name is impossible).
Article from Drupal Documentation.