"http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": {
"type": "people",
"id": "9"
}}},
"links": {
"self": "http://example.com/articles/1"
}
} // ...
Listing 1–5 JSON API Get Response
Zusätzlich bietet JSON:API jedoch eine für das Verbinden verschiedener Ressourcen extrem nützliche Funktionalität: Compound Documents. Dabei handelt es sich um die Repräsentation einer Ressource, die eine in Relation stehende Ressource inkludiert.
Somit kann im oberen Beispiel die Repräsentation des Artikels durch die Informationen des verlinkten Autors erweitert werden; ohne dass man dabei den Roundtrip eines zweiten Requests auf einen verlinkten Endpunkt machen muss.
// ...
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "The Force for Beginners"
},
"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"
}
},
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Obi-Wan",
"last-name": "Kenobi",
"profession": "Jedi-Master"
},
"links": {
"self": "http://example.com/people/9"
}
}],
} // ...
Listing 1–6 JSON API Compound Document
Dabei ist zu beachten: Das Relations-Objekt hält noch immer nur die ID des Autors, deren Repräsentation findet sich jedoch im included-Teil des Dokuments. Das ist wichtig, da bei multiplen Relationen auf dasselbe Objekt sonst eine hohe Redundanz entstehen würde. Generell kann man auch einfach im Request angeben, welche Relationen man in der Response inkludieren möchte. Dies gilt auch für eine Liste oder verschachtelte Ressourcenrelationen.
Ebenso kann die Repräsentation auf verschiedene Felder je Ressource verringert werden, um den Payload der Response noch weiter zu reduzieren. Solch ein flexibel gestalteter Request kann dann wie folgt aussehen:
GET /articles?include=author&fields[articles]=title,body
&fields[people]=name HTTP/1.1
Accept: application/vnd.api+json
Listing 1–7 JSON API Specified Request
Zudem bietet JSON:API noch die Möglichkeit für Paginierung, Sortierung und Filter im Request. Mit diesem Prinzip lässt sich also genau angeben, welche Ressource und deren Relationen in welcher Weise ausgegeben werden. Damit sind Ressourcen letztendlich nach ihren Relationen in Graphen abgebildet.
Auch für manipulative Funktionen wie das Erstellen, Ändern oder Löschen der Ressourcen kann man sich dieser Graphstruktur bedienen und mehrere Objekte in nur einem Request kaskadierend manipulieren. Man spart somit einige in klassischem REST sonst nötige Roundtrip-Requests.
1.4.4gRPC
Mit gRPC versucht Google, eine neue Renaissance des Remote Procedure Calls einzuleiten. gRPC wurde dabei speziell für die Kommunikation zwischen Microservices entwickelt. Laut Aussagen des Unternehmens ist es ein modernes, leichtgewichtiges Kommunikationsprotokoll, ein hochperformantes, universelles Remote-Procedure-Call-Framework, das über diverse Sprachen und allen bekannten Betriebssysteme hinweg funktioniert. Und es ist dazu noch Open Source.
Kein Wunder also, dass gRPC einiges an Aufsehen erregt hat. Spätestens seit es in die von vielen großen Firmen unterstütze Cloud Native Computing Foundation aufgenommen wurde. Bereits CoreOS, Cisco und nicht zuletzt Netflix stützen ihre interne Kommunikation auf dieses API-Framework [24].
gRPC in der Verwendung
In gRPC deklariert man einen Service in einer technologieunabhängigen Interface Defintion Language. Hierbei nutzt gRPC jedoch nicht wie weitläufig verbreitet JSON oder das noch von SOAP im RPC-Umfeld bekannte XML, sondern sogenannte Protocol Buffers. Eine solche Proto-Datei kann dann wie folgt aussehen:
syntax = "proto3";
package com.rebel;
message Rebel {
int64 id = 1;
string name = 2;
string ship = 3;
}
message GetRebelRequest {
int64 id = 1;
}
message GetRebelsViaShip {
string ship = 1;
}
service RebelService {
rpc Get (GetRebelRequest)
returns (Rebel) {}
rpc GetRebelsViaShip (GetRebelsViaShip)
returns (stream Rebel) {}
rpc GetMostDangerousRebel (stream GetRebelRequest)
returns (Rebel) {}
rpc GetRebels (stream GetRebelRequest)
returns (stream Rebel) {}
}
message RebelBase {
string name = 1;
map<int64, string> rebels = 2;
}
Listing 1–8 Proto-Service-Definition-Beispiel
Aus diesen definierten Proto-Dateien generiert gRPC mithilfe des protoc-Compilers technologiespezifische Bindungen. Durch diese generierten Stubs kann der Client einfach Funktionen des Servers aufrufen,