RESTful APIs richtig gemacht – Eine Anleitung für bessere REST-Schnittstellen

RESTful APIs richtig gemacht – Anleitung für bessere REST-Schnittstellen

REST mit JSON ist heutzutage der am weitesten verbreitete Ansatz für neue Programmierschnittstellen. Es gibt unzählige Quellen im Internet, wie Bücher, Vorträge, Blogs und andere Ressourcen, die sich mit diesem Thema beschäftigen. Dennoch scheint es große Meinungsunterschiede in der Entwicklergemeinde zu geben, wie Webschnittstellen gestaltet sein sollten. Die JSON-API-Spezifikation legt genau fest, wie ein RESTful API auf Basis eines einheitlichen Standards implementiert werden sollte. In diesem Artikel geben wir Ihnen eine kurze Einführung in das Thema und stellen Ihnen die wichtigsten Punkte vor, die Sie bei der Entwicklung von RESTful APIs beachten sollten.

RESTful Styleguide für JSON-APIs

Die JSON-API-Spezifikation liefert konkrete Vorschläge, wie ein gut gestaltetes REST API aussehen kann. Um Ihnen dies näherzubringen, begeben wir uns in die Welt der Sportvereine. Wir stellen uns vor, dass wir eine IT-Lösung entwickeln möchten, mit der wir Teams, Manager und Spieler verwalten können.

Datenmodell
Abbildung 1: Datenmodell

Das Diagramm (Abb. 1) zeigt die Entität “Team” und seine Beziehungen. Ein Team wird von einem Manager trainiert und mehreren Spielern zugeordnet. Manager und Spieler sind jeweils vom Typ “Person”.

Das API: Mit wem dürfen wir sprechen?

Das API bietet den Einstiegspunkt für unsere Beispielapplikation. Über einen API-URL-Pfad werden klassische Operationen wie Lesen, Schreiben, Ändern, Löschen oder die Verwaltung von Beziehungen ermöglicht. Der Pfad dient auch der Dokumentation für den API-Anwender und sollte daher klar und einfach zu verstehen sein. Als Einstiegspunkt für die Ressource “Team” bietet sich der Pfad “/teams” an. Der Pfadname ist in der Mehrzahl angegeben, da mehrere Teams gleichzeitig verwaltet werden können sollen. Wir legen daher fest, dass eine Entität über den Ressourcenpfad “/{entity-type}” verwaltet werden kann. Die Namenskonvention für einen Entity Type lautet: ein Nomen in der Mehrzahl, nur Kleinbuchstaben, einzelne Worte durch ein Minus getrennt.

  • Richtig: /teams, /team-memberships
  • Falsch: /Teams, /team, /teamMemberships, /team-search
LESEN  Aggressive Katzen: Wenn Stubentiger zu Raubtieren werden

Datenobjekte abfragen: Welche Mannschaften gibt es eigentlich?

Um eine Liste aller Teams abzurufen, wird ein einfacher HTTP GET auf den API-Pfad “/teams” durchgeführt.

GET /teams HTTP/1.1
Accept: application/vnd.api+json

Im HTTP-Header “Accept” wird festgelegt, dass die Antwort ein JSON-Dokument sein soll. Das zurückgelieferte Dokument enthält ein Array aller Team-Objekte. Eine mögliche Antwort könnte wie folgt aussehen:

{
  "data": [
    {
      "id": "1",
      "type": "teams",
      "attributes": {
        "name": "FC Norden Jugend",
        "category": "juniors"
      },
      "links": {
        "self": "http://example.com/teams/1"
      }
    },
    {
      "id": "2",
      "type": "teams",
      "attributes": {
        "name": "FC Essen",
        "category": "masters"
      },
      "links": {
        "self": "http://example.com/teams/2"
      }
    }
  ]
}

Die Dokumentstruktur: Ordnung ist das halbe Leben

Ein gut strukturiertes API hat auch eine einheitliche Dokumentstruktur. Die JSON-API-Spezifikation legt genau fest, wie diese Struktur aussehen sollte. Jedes Dokument enthält entweder das Element “data” oder das Element “errors”. Optional können auch die Elemente “meta”, “jsonapi”, “links” oder “included” auf oberster Ebene enthalten sein. Das Element “data” enthält die eigentlichen Daten und kann entweder aus einem einzelnen Resource-Objekt oder aus einem Array von Resource-Objekten bestehen. Zusätzlich können auch Objektreferenzen oder “null” enthalten sein.

Die JSON-API-Spezifikation legt außerdem fest, dass alle Ressourcen die Attribute “id” und “type” enthalten müssen, um eindeutige Identifikation zu gewährleisten. Die eigentlichen Daten werden im Element “attributes” geführt. Hier ein Beispiel:

{
  "data": {
    "id": "1",
    "type": "teams",
    "attributes": {
      "name": "FC Essen",
      "category": "masters"
    },
    "links": {
      "self": "http://example.com/teams/1"
    }
  }
}

Nach Datenobjekten suchen: Wo ist mein Team?

Um nach bestimmten Teams zu suchen, können Filterparameter verwendet werden. Die JSON-API-Spezifikation schlägt vor, die Filterparameter als Abfrageparameter an den API-Pfad anzuhängen. Der Parametername folgt dem Muster “filter[{attribut_name}]”. Hier ein Beispiel für die Suche nach dem Team “FC Norden”:

GET /teams?filter[name]=FC+Norden HTTP/1.1
Accept: application/vnd.api+json

Die Filterparameter können mit einem logischen UND verknüpft oder ein Set von Werten enthalten, was einem logischen ODER entspricht. Der reservierte Name “filter” klärt den Zweck des URL-Parameters sofort.

LESEN  Infrarotkabinen: Wärme und Entspannung für Körper und Geist

Im Ergebnis blättern: vor und zurück…

Um durch eine Ergebnisliste zu blättern, können assoziative Parameter verwendet werden. Diese Parameter bestehen aus “page[number]” und “page[size]”. Hier ein Beispiel:

GET /teams?page[number]=3&page[size]=10 HTTP/1.1
Accept: application/vnd.api+json

Im Antwortdokument werden die Links für das Blättern bereitgestellt. Die Angabe findet sich im “links”-Element parallel zu den eigentlichen Daten. So können Sie bequem durch die Seiten navigieren.

Ein neues Datenobjekt anlegen: Willkommen FC Oldenburg

Um ein neues Team anzulegen, verwenden Sie den API-Pfad “/teams” und die HTTP-Methode POST:

POST /teams HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "teams",
    "attributes": {
      "name": "FC Oldenburg",
      "category": "seniors"
    }
  }
}

Der Server wird eine eindeutige ID für das neu angelegte Team vergeben und sie im Antwortdokument zurückgeben.

Ein Datenobjekt lesen: Zeig mir das Team mit der Nummer 3

Um ein einzelnes Team abzurufen, verwenden Sie den API-Pfad “/teams/{team-id}” mit der entsprechenden Team-ID:

GET /teams/3 HTTP/1.1
Accept: application/vnd.api+json

Die Antwort enthält das gewünschte Team als JSON-Dokument.

Ein Datenobjekt ändern: FC Essen ist gealtert …

Um ein Team zu ändern, verwenden Sie ebenfalls den API-Pfad “/teams/{team-id}” und die HTTP-Methode PATCH:

PATCH /teams/2 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "id": "2",
    "type": "teams",
    "attributes": {
      "category": "seniors"
    }
  }
}

Nur die geänderten Attribute müssen im Request übertragen werden. Der Server wird das aktualisierte Team als Antwort liefern.

Ein Datenobjekt löschen: FC Oldenburg gibt’s nicht mehr

Um ein Team zu löschen, verwenden Sie den API-Pfad “/teams/{team-id}” und die HTTP-Methode DELETE:

DELETE /teams/3 HTTP/1.1

Als Antwort erhalten Sie einen entsprechenden HTTP-Statuscode, der den Erfolg des Löschvorgangs angibt.

Über Beziehungen: Wer gehört zu wem?

In unserem Beispielmodell kann einem Team ein Manager und mehrere Spieler zugeordnet werden. Um diese Beziehungen zu verwalten, verwenden wir das Konzept der Relationships. Hier ein Beispiel für die Beziehung “manager”:

GET /teams/3 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "id": "3",
    "type": "teams",
    "attributes": {
      "name": "FC Oldenburg"
    },
    "relationships": {
      "manager": {
        "data": {
          "type": "persons",
          "id": "55"
        },
        "links": {
          "self": "http://example.com/teams/3/relationships/manager",
          "related": "http://example.com/teams/3/manager"
        }
      }
    }
  }
}

Die Beziehungen werden im “relationships”-Element des Datenobjekts dargestellt. Die “data”-Eigenschaft enthält eine Referenz auf das Manager-Datenobjekt. Die Links ermöglichen es, die Beziehung selbst oder das zugehörige Manager-Dokument abzurufen.

LESEN  Tinder Gold: Ist es das Geld wert?

Fehlerbehandlung: Was läuft hier schief?

Um dem API-Benutzer über Fehler zu informieren, können Fehlermeldungen im “errors”-Element der Antwort geliefert werden. Hier ein Beispiel für einen Fehler mit HTTP-Statuscode 400 (Bad Request):

HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json

{
  "errors": [
    {
      "code": "100",
      "status": "400",
      "source": {
        "pointer": "data.attributes.name"
      },
      "title": "Mandatory field",
      "detail": "Attribute 'name' must not be empty"
    }
  ]
}

Die JSON-API-Spezifikation enthält weitere Attribute für Fehlerobjekte, die in dieser Antwort nicht verwendet wurden.

Fazit: Wir sind gewappnet

Die Entwicklung von RESTful APIs erfordert sorgfältige Planung und Umsetzung. Die JSON-API-Spezifikation bietet eine gute Grundlage für die Gestaltung von RESTful JSON APIs. Eine einheitliche Dokumentstruktur, klare Namenskonventionen und sinnvolle Verwendung von HTTP-Methoden und -Statuscodes helfen dabei, APIs zu entwickeln, die leicht verständlich, erweiterbar und wartbar sind. Mit den hier vorgestellten Konzepten können Sie erfolgreich RESTful APIs implementieren und Ihren Entwicklungsprozess verbessern.

Link zur JSON-API-Spezifikation
Link zum Buch “Build APIs You Won’t Hate”