REST-APIs verstehen und nutzen

APIs: Allgegenwärtig und jeder nutzt sie , wenn auch in direkt. Aber wofür sind sie da und wie funktionieren sie? Wir klären auf 💡

REST-APIs verstehen und nutzen
Photo by UX Indonesia / Unsplash

Vielleicht hast du schon einmal gesehen oder gehört, dass moderne Anwendungen REST-APIs verwenden. API ist die Abkürzung für Application Programmable Interface und ermöglichen die Kommunikation zwischen (verteilten) System. REST steht für Representational State Transfer. Im Klartext heißt das, dass alle Anfragen zustandlos sind. Für moderne Anwendungen bilden Sie also das Rückgrat der Kommunikation.

Grundlagen

Moderne Anwendungen unterscheiden sich sehr von "alten" System, wo alles auf einem Server liegt. Weswegen der Austausch von Daten immer wichtiger wird. Zudem werden die Systeme immer weiter getrennt oder verteilt und müssen daher Schnittstellen (Interfaces) anbieten, über die eine genormte Kommunikation erfolgt. Genau dafür sind REST-APIs eingeführt worden. Wikipedia definiert eine REST-API wie folgt:

Der Zweck von REST liegt schwerpunktmäßig auf der Maschine-zu-Maschine-Kommunikation. REST stellt eine einfache Alternative zu ähnlichen Verfahren wie SOAP und WSDL und dem verwandten Verfahren RPC dar. Anders als bei vielen verwandten Architekturen kodiert REST keine Methodeninformation in den URI, da der URI Ort und Namen der Ressource angibt, nicht aber die Funktionalität, die der Web-Dienst zu der Ressource anbietet. Der Vorteil von REST liegt darin, dass im WWW bereits ein Großteil der für REST nötigen Infrastruktur (z. B. Web- und Application-Server, HTTP-fähige Clients, HTML- und XML-Parser, Sicherheitsmechanismen) vorhanden ist und viele Web-Dienste per se REST-konform sind. Eine Ressource kann dabei über verschiedene Medientypen dargestellt werden, auch Repräsentation der Ressource genannt.

Quelle: https://de.wikipedia.org/wiki/Representational_State_Transfer

Wir werden hierbei uns aber nur auf die Kommunikation über HTTP beschränken.

Wie war es früher?

Früher (so bis ca. 2000/2005) war es teils so, dass (z.B. via PHP) die Daten direkt in das HTML auf dem Server projiziert worden sind und dann direkt mit den bereits gefüllten Daten an den Client ausgeliefert worden sind. Mobile Geräte oder Ähnliches gab es selten. Maschinen-zu-Maschinen-Kommunikation wurde z.B. Banken eher über SOAP, RPC oder WSDL (XML) durchgeführt.

Hier war die Kommunikationsrichtung in der Regel nur unidirektional vom Server zum Client:

Ein Nachladen von Daten war auch nicht (oder nur selten) notwendig weswegen die Webseiten recht statisch waren.

Wie ist es heute?

Mittlerweile sind aber nicht nur die Computer die einzigen die Daten konsumieren, sondern auch mobile Geräte (Handys, Autos, etc.) zudem gibt es nun auch Apps die auf Daten zugreifen müssen. Die Kommunikationen sind auch nicht mehr unidirektional sondern bidirektional, weil viele Daten auch nachgeladen werden (um First Contentful Paint zu verringern). Eine heutige Struktur würde (vereinfacht) wie folgt aussehen:

Und hierbei kommt dann die REST-API ins Spiel.

Welche Daten werden damit übertragen?

Prinzipiell können die APIs alle Art von Daten anbieten, geläufig sind aber tatsächlich lediglich textuelle Daten und Dateien.

Mit textuellen Daten meine ich jetzt nicht ein Buch oder etwas anderes. Nein. Hier meine ich strukturierte Daten. Für den Austausch von Daten hat sich der JSON-Standard mittlerweile (zum Glück 😑) sehr gut etabliert. Er definiert wie die Daten auszusehen haben, damit sie alle Systeme verstehen und auswerten können. Auch können APIs Dateien wie Bildern oder anderen Formaten bereitstellen.

Wie sind APIs aufgebaut?

REST-APIs sind nach gewissen Schemata aufgebaut, wodurch sie sich einfach adressieren lassen.

Im Wesentlichen bieten APIs gewisse Schlüsselworte, womit man definiert, was man machen möchte. Diese basieren auf auf dem HTTP-Standard-Methoden. Hier gibt es fünf Stück, die im täglichen Einsatz oft verwendet werden GET, POST, PATCH, PUT und DELETE. Damit lassen sich alle CRUD-Aktionen abbilden.

Um das zu veranschaulichen, schauen wir uns einmal die PokéAPI an. Hiermit lassen sich Pokémon von dem Dienst abfragen.

Grundlegend kann man sagen, dass APIs immer wie folgt aufgebaut sind:

https://server.com/[eventuelle Version]/{ressource}/{id}

Eine Ressource ist immer eine Entität, z.B. ein Pokémon, Auto, Artikel oder Benutzer.

Eventuelle Abweichungen können durch den API-Anbieter vorhanden sein. Daher prüfe bitte immer vorab die Doku deines Anbieters.

Daten über eine API anfordern

Wir möchten nun Daten über ein gewisses Pokémon haben. Ich mag Mew eigentlich ziemlich gerne und möchte nun ein paar Daten für Mew haben. Alle (guten) API-Anbieter dokumentieren ihre API und zeigen, wie du sie nutzen kannst, teils auch mit Beispielen. So auch die PokéAPI:

Documentation - PokéAPI

Hierfür bietet sich die Methode GETan, mit der wir den Server anweisen uns Daten zu geben.

In der Dokumentation der PokéAPI finden wir hierzu auch einen Ressourcen-Endpunkt:

HTTP GET https://pokeapi.co/api/v2/pokemon/{id or name}/

Heißt, dass wir mit dem Endpunkt ein bestimmtes Pokémon mit einer ID oder einem Namen abfragen können.

Normale Anfragen via GET können auch im Browser ausgeführt werden, da ein Abfragen einer normalen Webseite auch über ein GETgeschieht.

Geben wir nun also die obere URL für Pickachu ein, bekommen wir folgende Rückgabe:

Generell lassen sich damit auch alle Pokémon abfragen, in dem man den Parameter IDoder Nameeinfach weg lässt (https://pokeapi.co/api/v2/pokemon):

Hier gibt es nun eine kleine Spezialität, die aber in der Regel alle APIs haben, nämlich ein Paging.

Seitenweises Abrufen von Daten

Werden viele Daten auf einmal benötigt, setzt das natürlich den Server des API-Anbieters unter Last, die auch sehr vielen Datensätzen, sehr groß werden kann. Um genau dem entgegen zu wirken, arbeiten APIs häufig mit Pagings. Also dem Seitenweisem Zurückliefern von Daten.

💡
Ob und in wie weit dein API-Anbieter Paging unterstützt, erfährst du in der Regel in der Dokumentation der API.

In dem oberen Beispiel aller Pokémons sehen wir also, dass es insgesamt 1292 Pokémons geben würde. Das wäre auch viel zu viel für eine Anfrage. Darum beinhalten viele APIs (die ich bisher kennengelernt habe) auch entsprechende Befehle zum Durchlaufen der Seiten. Meistens geschieht dies über den Query-String, dem man dann Parameter zur Seite und zur Anzahl der Ergebnisse übergeben kann. Zusätzlich werden in der Regel auch die nächste Seite und die vorherige Seite als Link zurück geliefert, damit der Entwickler die aktuelle, vorherige und nachfolgende Seite nicht manuell "berechnen" muss im Code.

Eine Seite der PokéAPI für Pokémons kann man mit der nachfolgenden URL abrufen:

GET https://pokeapi.co/api/v2/pokemon?offset=20&limit=20

Hier gibt es den offset als Parameter (der Anfangspunkt der Seite) und ein Limit (für die Anzahl der liefernden Daten). Und so könnte man nun durch alle Seiten durchlaufen.

Und wenn ich Daten verändert oder löschen muss?

Hierbei kommt es auch wieder sehr auf deinen Anbieter der API an, ob er solche Aktionen unterstützt. Auch das kannst du in der Doku erlesen.

Die PokéAPI bietet keine Endpunkte zum Ändern der Daten an, was aber auch völlig klar ist, weil es ja eine lesende Datenbank ist. Möchtest du aber nun tatsächlich Daten schreiben, kommt es sehr stark darauf an, was genau du tun möchtest.

💡
Für die weiteren Schritte tun wir einfach so, als könne die PokéAPI auch Daten verändern. Das soll auch nur das Prinzip widerspiegeln, wie es grundlegend funktionieren würde.

Datensatz anlegen

Zum Anlegen von Daten wird die Methode POSTverwendet. Diese legt per Definition einen neuen Datensatz zur Ressource an. Damit der POSTklappt, muss man natürlich auch noch entsprechende Daten mitgeben. Das geschieht in der Regel über den BODYeiner Anfrage.

Im Vergleich zu einem GETbesitzt der POSTnämlich einen Anfragen-Body, der die zu verwendenden Daten enthält und mit an die API gesendet werden muss. Solche Anfragen können in der Regel nicht einfach über den Browser durchgeführt werden, sondern sollten durch cURL, Postman oder eine Programmiersprache deiner Wahl gemacht werden. Aber ein Aufruf zum Hinzufügen des Pokémon Marfra könnte so aussehen:

HTTP POST https://pokeapi.co/api/v2/pokemon
HTTP PATCH https://pokeapi.co/api/v2/pokemon/{id}

Content-Type: application/json
{
  "name": "Marfra"
  ...
}

Hier ist es wichtig zu beachten, dass man das Format für den Body angibt. Für JSON wäre dieses Format application/json.

Wenn alles geklappt hat, dann wäre nun ein neues Pokémon angelegt worden und du bekommst in der Regel eine ID oder einen Bezeichner zurück geliefert, mit der du weitere Anfragen machen könntest. Falls nicht, bekommst du aber in der Rückgabe der Anfrage irgendein Hinweis darauf, warum es nicht geklappt hat.

Datensatz verändern

Hier verhält es sich ähnlich wie mit dem Anlegen, nur, dass wir auch noch angeben müssen, welches Pokémon wir verändern wollen. Die verwendete Methode wäre in dem Falle PATCH. Wichtig ist, dass wir auch definieren müssen, welches Pokémon wir editieren möchten. Das geschieht in der Regel durch eine ID, Namen oder einem anderen eindeutigen Bezeichner.

Möchten wir nun unser Pokèmon um die Ability Super-Nerd anreichern, könnte unsere Anfrage wie folgt aussehen:

HTTP PATCH https://pokeapi.co/api/v2/pokemon/{id}

Content-Type: application/json
{
  "ability": "Super-Nerd"
}

Wenn alles geklappt hat, dann wäre nun das bestehende Pokémon Marfraverändert worden. Falls nicht, bekommst du aber in der Rückgabe der Anfrage irgendein Hinweis darauf, warum es nicht geklappt hat.

Zur Änderung müssen nicht alle Daten vorliegen, hier reicht es aus, wenn der Body der Anfrage das zu ändernde Feld beinhaltet. Auch hier kann es Abweichungen geben, die der API-Anbieter festgelegt hat.

Datensatz löschen

Hierzu können wir die Methode DELETEverwenden. Im Gegensatz zu einem POSToder PATCHbesitzt diese Methode keinen Body. Brauchen wir auch nicht, weil wir die Ressource über den eindeutigen Bezeichner identifizieren müssen. Hier kommt wieder unsere IDins Spiel. Unser Marfra-Pokémon könnten wir mit dem Befehl löschen:

HTTP DELETE https://pokeapi.co/api/v2/pokemon/{id}

Sonderfall PUT

Es gibt streng genommen noch einen weiteren Fall, nämlich PUT. Er kennzeichnet sich dadurch, dass er per Definition dafür geeignet ist, einen Datensatz mit allen Daten zu updaten bzw. zu ersetzen. Manchmal wir diese Methode auch als "Upsert" verwendet, der den Datensatz verändert, sofern es ihn gibt, alternativ anlegt. Das kommt aber sehr stark auf den API-Anbieter an.

Für PUTmuss immer das gesamte zu ändernde Objekt (mit den Änderungen) übermittelt werden.

HTTP PUT https://pokeapi.co/api/v2/pokemon/{id}

Content-Type: application/json
{
  "name": "Marfra"
  "ability": "Super-Nerd"
}

Status-Codes prüfen

Wenn deine API alles richtig gemacht hat oder aber etwas schiefläuft, dann wird sie dir das auch (mehr oder weniger genau) sagen. Hier ist es wichtig auf die Status-Codes zu schauen, die aus dem HTTP-Standard entspringen.

Geläufige Status Codes wären:
- 200 = OK, Ressource gefunden und Methode ausgeführt
- 404 = Ressource nicht gefunden
- 5XX = Es gab einen Serverfehler

Der JSON-Standard

Wie Ihr seht, ist es recht einfach Daten abzufragen und Daten an ein System zu geben. Kommen wir aber mal zum Standard, der mittlerweile für die Ablage, Abrufen und Übermitteln von Daten großflächig genutzt wird: JSON

Nein, es hat nichts mit Jason Statham zutun, auch wenn das gerne als kleiner Running Gag verwendet wird.

JSON steht für JavaScript Object Notation welches 1997 erstmalig von Douglas Crockfordt spezifiziert worden ist. Mittlerweile gibt es dazu auch einen RFC (Request for Comment) 8259 und eine ECMA-404.

Die Daten innerhalb eines JSON können beliebig tief geschachtelt werden und bestehenden Grundsätzlich aus folgenden Datentypen:

  • Nullwert = NULL (leer)
  • Boolescher Wert = true/false
  • Zahlen = negative/positive, Dezimal- als auch Ganzzahlen oder mit Angabe von Exponenten (ebzw. E)
  • Zeichenketten = "Ich bin eine Zeichenkette" (ggf. mit Escaping via \)
  • Array = Indizierte Listen beginnend mit [und endend mit ])
    z.B. [1, 2, 3, 4, 5]
  • Objekt = Komma geteilte, ungeordnete Listen von Eigenschaften
    z.B. { "name": "wert", "anzahl": 1, "werte": [ 1, 2, 3, 5, 4] }

Alle Typen können mit einander vermischt und unendlich tief (theoretisch) verschachatelt werden. Im Gegensatz zu XML bietet es weitaus weniger Overhead.

Gerade im JavaScript-Bereich hat sich dieser Standard als "Goldstandard" entwickelt und wird auch zum Teil verwendet, um Dateien abzulegen, die viele Informationen enthalten und durch Programme/Anwendungen geschrieben und gelesen werden.

Fazit

Du hast nun einen kleinen Einblick in die Welt von APIs, JSON und die vielfältigen Möglichkeiten bekommen, wie man APIs konsumiert und wie Systeme heutzutage mit einander plaudern. Viel Spaß beim "Anzapfen" von APIs und Nuten der Daten 🔥

💡
Speichere dir am besten diesen Artikel ab. Viele meiner weiteren Artikel basieren auf der Nutzung von APIs und/oder der Nutzung von JSON-Daten.

Und, hast du schon Ideen welche APIs du verwenden kannst oder hast du sogar schon eine eigene API? Lass es mich in den Kommentaren wissen 👇