Was REST APIs von HTML lernen können.

7 minute read Published:

Selbstbeschreibende APIs mit Hypermedia.

Die beste Implementierung des REST Konzeptes ist HTML in Kombination mit HTTP. HTML erlaubt es nicht nur Daten zwischen Client und Server auszutauschen, es enthält auch Informationen in welcher Beziehung diese Dokumente einander stehen (Links) und welche Aktionen durchgeführt werden können (Formulare). Daruch werden generische Clients für HTML möglich: Web-Browser.

Mit diesen Mitteln ist das Web groß geworden. Wir können aufwendige serverbasierte Anwendungen, wie Foren oder Shops betreiben, ohne eine einzige Zeile JavaScript ausführen zu müssen. Diese entsprechen vielleicht nicht heutigen Maßstäben an Komfort, aber sie funktionieren.

JSON APIs

Was die Entwicklung von REST basierten APIs angeht, haben wir jedoch wieder einen gewaltigen Rückschritt gemacht. Hier werden in den meisten Fällen nur Daten ausgetauscht, die keinerlei Semantik enthalten.

Ein Beispiel:

{
   "id": 12,
   "title": "Clean Code",
   "price": 49.99,
   "orderable": true
}

Dadurch wandert ein Großteil der Logik in den Client. Dieser muss wissen, wie diese Daten in der Beziehung zu einander stehen, was man mit ihnen tun kann und wie dies geschehen soll.

Wenn wir ein eigenes Format auf der Basis von XML oder JSON für unsere APIs verwenden. bewegen wir uns auf der gleichen Ebene wie XML oder SGML, die als Basis für HTML dienen.

Für jeden Dokumententyp muss man so einen neuen Parser schreiben. Dies ist für Benutzer dynamisch typisierter Sprachen nicht so spürbar, weil sie immer eine Datenstruktur bekommen mit der sie direkt arbeiten können. Als Entwickler einer statisch typisierten Sprache muss ich vorher das Mapping von JSON zu Klassen von Hand definieren. Es ist unmöglich hier einen generischen Client zu schreiben.

Wenn man Glück hat werden mögliche Aktionen durch Flags als Teil des Dokumentes gekennzeichnet. Im oberen Beispiel als orderable. Wie genau eine Bestellung durchgeführt werden kann, oder wie dies zu interpretieren ist, wird hier nicht beschrieben.

Da dies von jedem Client neu implementiert werden muss, entsteht hier nicht nur zusätzlicher Aufwand, sondern auch doppelter Code. Und wie wir wissen:

“Duplicate code is the root of all evil in software design.” - Robert C. Martin

Ein Entwickler benötigt zur korrekten Implementierung eines Clients nicht nur eine API Dokumentation, sondern auch detailliertes Wissen zur Geschäftslogik.

Hypermedia

Nun gibt es die Bestrebungen den Hypermedia Aspekt von HTML auch in REST-APIs zu übernehmen. Dokumente werden mit Links angereichert. Ein Entwickler muss nun nicht mehr die URIs in seiner App hart kodieren und die Api wird dadurch flexibler.

{
   "id": 12,
   "title": "Clean Code",
   "price": 49.99,
   "_links": {
     "self": "https://example.com/books/12",
     "author": "https://example.com/authors/Robert-C.-Martin",
     "add_to_cart": "https://example.com/cart"
   }
}

Die self Relation gibt uns an unter welcher URI das Dokument selbst abgerufen werden kann. Dies muss sich ein Client nach dem Abruf nicht nur nicht mehr selbst merken, die Information bleibt auch erhalten, wenn das Dokument gespeichert oder weitergegeben wird.

Oft werden auf diese Art und Weise auch mögliche Aktionen abgebildet. Statt, wie beim Beispiel ohne Hypermedia, orderable gibt es nun einen Link add_to_cart. Dies gibt uns immerhin an wo wir diese Aktion ausführen können, aber noch nicht wie. Zudem ist die Bedeutung der Links in einer Liste nicht direkt erkennbar, da es sich um Aktionen oder Verknüpfungen handeln kann.

Hierdurch entfällt zwar die Notwendigkeit im Client selbst URIs zusammenbauen zu müssen, aber eigentlich ist die Welt für Entwickler nicht viel besser geworden. Dies könnte auch die mangelnde Verbreitung von Hypermedia erklären. Was wirklich interessant ist und auch HTML so mächtig macht ist, meiner Ansicht nach, die Frage nach dem Wie.

Hypermedia - Mit Aktionen

Es gibt wenige Formate, die versuchen die Verlinkungen durch Aktionen anzureichern. Diese würden bei HTML durch Formulare abgebildet. Mir bekannt sind Mason und Siren. Das Nachfolgende Beispiel basiert auf Siren.

{
  "class": [ "shop-item" ],
  "rel": [ "http://example.com/rels/shop-item" ],
  "properties": [
    { "key": "id",    "value": 12,           "label": "ID" },
    { "key": "title", "value": "Clean Code", "label": "Title" },
    { "key": "price", "value": "$49.99",     "label": "Price" }
  ],
  "entities": [
    {
      "class": [ "author", "info" ],
      "properties": [
        { "key": "name", "value": "Robert C. Martin", "label": "Author" }
      ],
      "rel": [ "http://example.com/rels/author" ],
      "href": "https://example.com/authors/Robert-C.-Martin"
    }
  ],
  "actions": [
    {
      "name": "add-to-cart",
      "title": "Add to Cart",
      "method": "PUT",
      "href": "https://example.com/cart/items/12",
      "type": "application/x-www-form-urlencoded",
      "fields": [
        { "name": "itemId",   "type": "hidden", "value": "12" },
        { "name": "quantity", "type": "number", "title": "Quantity" }
      ]
    }
  ],
  "links": [
    { "rel": [ "self" ],     "href": "https://example.com/books/12" },
    { "rel": [ "previous" ], "href": "https://example.com/books/11" },
    { "rel": [ "next" ],     "href": "https://example.com/books/13" }
  ]
}

Jedes Siren Dokument hat das selbe zugrundeliegende Schema. Es enthält auf der obersten Ebene die Elemente class, properties, entities, actions und links.

Class

Die Klasse beschreibt welche Art von Inhalt sich im Dokument befindet. Ein Dokument kann auch mehrere Klassen beinhalten.

Properties

Hier sind unsere Daten enthalten, die wir in unserem eigenen Format meistens auf der obersten Ebene abgebildet hätten. Die Struktur ist flach. Komplexere Objekte oder Listen bilden eigene Entitäten und werden unter entities abgebildet.

Entities

Hier ist die Verknüpfung zu anderen Entitäten beschrieben. Diese können einen Teil ihrer Properties und eine Verknüpfung enthalten.

Actions

Hier sind alle Aktionen beschrieben, die im Kontext des Dokuments möglich sind. Hier wird im Stil eines HTML-Formulars auch beschrieben, wie diese Aktionen durchgeführt werden können. Ein Client kann diese nutzen, um dem Benutzer ein Formular zu generieren und diese Aktion beim Server durchzuführen.

Wo andere Formate hier einen Großteil der Informationen untergebracht hätten, werden hier nur triviale Verknüpfungen zur Navigation beschrieben. In diesem Fall sind es der Verweis auf die eigene URI und eine Paginierung.

Generischer Client

Mit einem solchen Format ist es möglich einen generischen Client zu implementieren. Unabhängig von der eigentlichen Geschäftslogik könnte dieser eine Navigationsstruktur, Daten und Aktionen mit individuellen Formularen abbilden.

Wie bei HTML wären hier die hier beschriebenen möglichen Aktionen und Daten je nach den Rechten eines Benutzers unterschiedlich. Ein normaler Nutzer des Shops würde vielleicht die Möglichkeit haben ein Produkt in eine Warenkorb zu legen oder eine Bewertung abzugeben. Als Benutzer mit erweiterten Rechten würde das Dokument jedoch zum Beispiel auch Aktionen für das Löschen oder Bearbeiten beinhalten.

Zudem gibt es, am Beispiel von Siren, nur noch ein Format, das ein Client verstehen muss. Statt individuelle Dokumente in Klassen abzubilden, lässt sich, analog zu HTML, ein einheitlicher Siren-Parser implementieren.

Ein Client Entwickler muss sich nur noch darauf konzentrieren, wie die Dokumente dargestellt werden sollen.

Generische Clients

Da hier keine Informationen zur Art der Darstellung enthalten sind würde sich ein generischer Client auf die Darstellung von Listen, Formularen und Links beschränken. Dies ist sicher nichts was man einem Endkonsumenten geben würde. Aber auch bei einer solchen Zielgruppe müsste sich der Anwendungsentwickler nur noch um die Art der Darstellung der einzelnen Dokumenttypen kümmern. Jegliches Wissen zur Geschäftslogik wird Teil des Dokumentes oder ist beim Server implementiert.

Ein generischer Client für ein Hypermediaformat ist jedoch besonders für firmeninterne Tools interessant. Sobald man einen Geschäftsprozess in einer API abgebildet hat, kann man diesen bereits mit einem funktionierenden Client benutzen. Dieser mag nicht der hübscheste sein, es muss aber erstmal kein Geld für eine angepasste Lösung ausgegeben werden. Auch durch die Beschreibung der Logik im Dokument können Kosten bei der Entwicklung gespart werden.

Dokumentation

Da bei Hypermedia die URIs dynamisch sind stehen hier die Dokumente im Fordergrund. Für den Konsumenten der API werden nicht mehr die einzelnen Routen beschrieben, sondern die Dokumente, welche die API zur Verfügung stellt.

Fazit

Meiner Ansicht nach sind aktuelle auf Verlinkung fokussierte Formate zwar ein Fortschritt, aber noch immer den semantischen Möglichkeiten von HTML deutlich unterlegen. Beschreibt man jedoch zudem mögliche Aktionen auf dem Niveau von HTML wird dieses Konzept auch im API-Kontext sehr mächtig. Es ermöglicht die Implementierung von generischen Clients und reduziert doppelten Code.

Bei Hypermedia-Formaten kommt oft die Frage nach standardisierten Formaten auf. Natürlich wäre es wünschenswert ein HTML für JSON APIs als Standard zu haben. Soweit sind wir jedoch noch nicht. Hier möchte ich zum Mut raten. Bisher folgen die JSON-Dokumente in den meisten APIs auch keinem Standard. Dies wird jedoch akzeptiert. Selbst wenn man sich ein eigenes Format ausdenkt ist dies in jedem Fall ein Fortschritt, wenn es einem erlaubt die Logik in den Clients zu entfernen und generische Implementierungen zu ermöglichen.