Kernconcepten
JSON:API heeft veel concepten in de specificatie, die hier niet allemaal gedocumenteerd zijn. Gebruikers van de module hoeven echter niet alle concepten van de specificatie volledig te begrijpen om productief te kunnen werken met deze module. Als je wél dieper wilt ingaan op hoe de documenten van JSON:API zijn opgebouwd, waarom de module iets op een bepaalde manier doet, of gewoon meer wilt leren over het ontwerp van de module, dan worden lezers aangemoedigd om de specificatie te lezen op jsonapi.org.
Documentstructuur
JSON:API is zeer uitgesproken over hoe JSON-documenten moeten worden opgebouwd en welke informatie in elk request- en/of response-body moet staan.
Elk request/response-body moet onder één enkel JSON-object vallen.
{
// je data hier...
}
De data, of de informatie die specifiek is voor een resource of meerdere resources, moet binnen dit top-level object leven onder het data
“member”. Een “member” is gewoon een vooraf gedefinieerde key in het JSON-object. Het data
member kan ofwel een object ({}
) zijn of een array ([]
). Bij het aanmaken of bijwerken van een resource zal dit altijd een enkel object ({}
) zijn dat een enkel item voorstelt. Alleen bij het ophalen van een “collectie” van meerdere resources zal deze property een array zijn.
{
"data": {
// Je resource data komt hier.
}
}
Andere top-level members zijn: errors
, meta
, links
, en included
. Hiervan zal included
het vaakst worden gebruikt, maar dit wordt later in de documentatie behandeld.
Voor meer informatie over de top-level structuur kun je de specificatie raadplegen.
Binnen de data
- en included
-members staan “resource objects” of “resource identifier objects”. “Resource objects” vertegenwoordigen de inhoud van de resources (entiteiten) waar je mee werkt. “Resource identifier objects” zijn zoals foreign keys in een database; ze identificeren een resource zonder de velden van die resource te bevatten. In Drupal-termen is een resource object meestal de JSON-representatie van een enkele entiteit, dat kan een node zijn, een taxonomieterm of een gebruiker. In Drupal-termen is een resource identifier gewoon genoeg informatie om een entiteit te laden—je hebt het type en het ID, en niets meer.
Elk resource object moet twee members bevatten: type
en id
. De enige uitzondering hierop is bij het aanmaken van een nieuwe entiteit, in dat geval mag het id
worden weggelaten om Drupal een id te laten genereren voor de nieuwe resource. Het is echter volledig mogelijk dat de clientapplicatie een UUID aan de resource meegeeft bij het aanmaken van een nieuwe entiteit. Alle ID’s in JSON:API zijn UUID’s.
Het type
member is altijd verplicht. De waarde voor het type-member is afgeleid van de naam van het entiteitstype en de bundle, waar van toepassing. Het type voor een entiteitsresource volgt altijd het patroon entity_type--bundle
. Bijvoorbeeld: de core node-typen article en basic page worden weergegeven als: node--article
en node--page
.
Dus, op een entiteit zonder verplichte eigenschappen of velden, kan men een nieuwe entiteit aanmaken met de volgende JSON:
{
"data": {
"type": "node--my-bundle",
}
}
Dit zou echter niet erg nuttig zijn. We moeten echte waarden voor de entiteit opnemen. Om dit te doen heeft JSON:API twee members voor waarden: attributes
en relationships
. attributes
slaan waarden op die specifiek zijn voor de onderliggende resource. relationships
zijn waarden die behoren tot een andere resource in het systeem. In Drupal-termen vertegenwoordigen relationships
meestal waarden die worden opgeslagen door een entiteitsreferentie. Op de core article-bundle van Drupal kan dit de uid
property zijn. Dit komt omdat de uid
property een entiteitsreferentie is naar de gebruiker die het artikel heeft geschreven. De body van een document met attributes
en relationships
kan er zo uitzien:
{
"data": {
"type": "node--my-bundle",
"id": "2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e",
"attributes": {
"title": "Een voorbeeld"
},
"relationships": {
"uid": {
"data": {
"type": "user--user",
"id": "53bb14cc-544a-4cf2-88e8-e9cdd0b6948f"
}
}
}
}
}
Zoals je ziet staat de uid
property onder het relationships
-member. Net als de hoofdresource bevat het ook een type
- en een id
-member, omdat het een aparte en afzonderlijke resource is.
Merk op dat uid
geen attributes
of relationships
heeft. Dit komt omdat JSON:API de inhoud van een relatie niet opneemt tenzij hier expliciet om gevraagd wordt via een speciale queryparameter, include
. Meer hierover later in de documentatie (zie “Resources ophalen (GET)”).
Voor meer details over de structuur van resource objects kun je de specificatie raadplegen.
§ “Virtuele” resource-identificatoren
In sommige omstandigheden staat Drupal toe dat een relatie naar een resource verwijst (een entiteitsreferentie naar een entiteit) die niet in de database is opgeslagen en daarom niet via JSON:API kan worden opgehaald. De “virtuele” resource-identificator kan verschillende omstandigheden aanduiden afhankelijk van de context, maar verwijst altijd naar een resource die niet kan worden gevonden.
Gebruik en betekenis van de ‘virtuele’ resource-identificator in Drupal core
Het taxonomietermveld parent
is het meest opvallende voorbeeld van dit speciale geval in Drupal core. Dit relatieveld kan een resource-identificator bevatten voor een “virtuele” taxonomieterm-resource. In dit geval identificeert de “virtuele” resource-identificator de <root>
taxonomieterm. Dit geeft aan dat de verwijzende term zich op het hoogste niveau van zijn vocabulaire bevindt.
Bekijk bijvoorbeeld het volgende response-document voor een hypothetische taxonomieterm:
{
"data": {
"type": "taxonomy_term--tags",
"id": "2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e",
"attributes": {
"name": "Politiek"
},
"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": "Gebruik en betekenis van de ‘virtuele’ resource-identificator."
}
}
}
}
}
]
}
}
}
}
Merk op dat de parent
-relatie van deze Term
(een entiteitsreferentieveld) een resource identifier object heeft waarvan het id
geen UUID is, maar "virtual"
. Dit is noodzakelijk omdat een top-level of root-level Term
een verwijzing heeft naar een niet-opgeslagen <root>
-term (target_id = 0)
als ouder.
Waarom?
Aangezien de root-term niet wordt opgeslagen en een Term
meer dan één ouder kan hebben, is de cruciale vraag: hoe onderscheiden we een Term
die:
- alleen
Term
3
als ouder heeft ([3]
) ? - zowel deze niet-opgeslagen root
Term
als ouder én eenTerm
3
([0, 3]
) heeft?
Het antwoord is dat als JSON:API de niet-opgeslagen root-term 0
zou weglaten in plaats van het "virtual"
-ID te gebruiken, het niet mogelijk zou zijn die twee gevallen te onderscheiden!
§ “Ontbrekende” resource-identificatoren
Drupal “ruimt” relaties naar verwijderde resources niet op (entiteitsreferentievelden die verwijzingen hebben naar entiteiten die zijn verwijderd). Met andere woorden: Drupal laat “zwevende” relaties (entiteitsreferenties) staan.
Wanneer JSON:API zulke zwevende relaties tegenkomt, gebruikt het de “ontbrekende” resource-identificator.
Gebruik en betekenis van de ‘ontbrekende’ resource-identificator in Drupal core
Blijvend bij het voorbeeld van de ‘virtuele’ resource-identificator: het taxonomietermveld parent
. Stel dat een bepaalde taxonomieterm vroeger de taxonomieterm “België” als ouder had, maar dat de resource van de taxonomieterm “België” nu niet meer bestaat — misschien omdat het kleine land België ophield te bestaan. Dan zou dit relatieveld een resource-identificator bevatten voor een “ontbrekende” taxonomieterm-resource.
Bekijk bijvoorbeeld het volgende response-document voor een hypothetische taxonomieterm:
{
"data": {
"type": "taxonomy_term--tags",
"id": "2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e",
"attributes": {
"name": "Politiek"
},
"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": "Gebruik en betekenis van de ‘ontbrekende’ resource-identificator."
}
}
}
}
}
]
}
}
}
}
Merk op dat de parent
-relatie van deze Term
(een entiteitsreferentieveld) een resource identifier object heeft waarvan het id
geen UUID is, maar "missing"
. Bovendien is het type
unknown
(omdat Drupal de bundle van de verwezen entiteit niet opslaat, alleen het entiteitstype, en dus het bepalen van de JSON:API-resource-typenaam onmogelijk is).
Artikel uit de Drupal-documentatie.