Die erste API die ich geschrieben habe, waren wirklich schlecht, aber ihre Spezifikation und Dokumentation war sogar noch schlechter. Ich konnte die API kaum mehr überblicken und die auf mich zukommenden Probleme nicht erkennen.
Ich sollte wieder eine API schreiben, und ich wollte unbedingt eine bessere Lösung als für die letzten.
Einer meiner Kollegen empfahl mir Swagger.io und ein anderer sagte, + ich sollte Apiary.io ausprobieren, die auf der Blueprint Spezifikation aufsetzt.
Swagger empfand ich als eher verwirrend. Es verwendet YAML, was ich nicht so richtig mag. Blueprint verwendet Markdown, mit dem ich sowieso ziemlich viel arbeite. Obwohl Apiary ein kommerzielles Tool ist, wollte ich es ausprobieren. Es gibt weiterhin Open-Source-Werkzeuge, die Dokumentation aus dem Quellcode generieren können. Falls Apiary irgendwie nicht mehr klappt, könnte ich immer noch auf die Open-Source-Werkzeuge zurückgreifen.
Letztlich war Apiary richtig gut, und ich kann es nur empfehlen wenn man mit Blueprint anfangen möchte. Blueprint selbst ist serh nett und brauchtbar, aber es hat seine Ecken und Kanten. Das Format ist aber glücklicherweise auch weiterhin in Entwicklung.
Alles beginnt zunächst mit einem Header:
FORMAT: 1A
HOST: http://api.grobmeier.de/
# Meine API
Die erste Zeile zeigt den Tools, das es sich hier um eine Blueprint Datei handelt. Die zweite Zeile nutzt Apiary, um die Möglichkeit bereitzustellen direkt aus dem Editor gegen die implementierte API zu testen.
Die erste Markdown Überschrift ist dann der Name der API. Danach beginnt der Spaß.
Wir beginnen damit, eine Sammlung (Collection) von Resourcen zu deklarieren.
## Person Collection [/persons]
Wie Sie sehen, hat die Collection eckige Klammern am Ende, die die Resource benennen. Eine Collection hat zwei Gatter am Anfang stehen, was in Markdown “Überschrift, Level 2” bedeutet.
Diese Resource kann schon benutzt werden, um eine neue Person zu erstellen.
### Erstelle eine neue Person [POST]
Wir nutzen die gleiche Resource, die wir bereits in der Collection benannt haben. Daher müssen wir nichts weitermachen, als POST zu erklären, und dann wird ein POST an die Route /persons angenommen.
Hier ist, was ich als Request und Response sehen möchte:
+ Request (application/json)
{
"name": "Christian",
"surname": "Grobmeier"
}
+ Response 200 (application/json)
{
"id": 1235,
"name": "Christian",
"surname": "Grobmeier"
}
Request und Response sind beides Keywords und Teil einer Markdown-Liste. Ich habe ausserdem den Content-Typen mit definiert, und im Falle der Antwort sogar noch den HTTP-Code 200.
Die Payload ist JSON, als (in Markdown) “pre-formatted” Source-Code. Hier bitte aufgepasst, es muss ordentlich eingerückt werden, damit das auch erkennt wird. Hier ist der Apiary Editor wieder eine große Hilfe.
JSON überall in der API Spezifikation zu verteilen ist nett und auch lesbar für kleinere Projekte, aber wenn das Projekt größer wird, könnte es Probleme geben. Dafür gibt es eine Lösung, die sich MSON nennt. Dazu mehr im nächsten Blog-Post - Link siehe am Ende.
In Apiary sieht das so aus.
Einfach, nicht wahr? Dann sehen wir mal, wie wir die Person 123 auslesen können.
Wenn man die Daten eines bestimmten Objekts mit der ID 123 haben möchten, würde man überlicherweise eine GET-Anfrage an /person/123 senden.
Genau das sollten wir nun auch machen. Ich habe folgendes in die Kollektion mit angelegt.
### Person lesen [GET /persons/{id}]
Wie Sie sehen, überschreiben wir die Collection URL und fügen eine ID hinzu. Dieser Parameter ist mit geschweiften Klammern gekennzeichnet.
We brauchen keinen Request-Body (und sollten mit GET auch keinen senden), also schreiben wir auch nichts weiter dazu.
Aber wir müssen definieren, was {id} eigentlich ist. Das machen wir mit einem Parameter-Block.
### Get a person [GET /persons/{id}]
+ Parameters
+ id: 123
+ Response 200 (application/json)
{
"id": 123,
"name": "Christian",
"surname": "Grobmeier"
}
Wie Sie sehen, sind die Parameter mitsamt Beispiel definiert. Das funktioniert auch mit mehreren URL Parametern.
Zum Beispiel:
### Person lesen [GET /persons/{id}/{name}]
+ Parameters
+ id: 123
+ name: Christian
Aber wie kann man optionale Parameter festlegen, wie es mit Query Strings durchaus passieren kann?
Einfach:
### Person lesen [GET /persons/{id}{?include}]
+ Parameters
+ id: 123
+ include: metadata
Mit dem obigen Konstrukt machen wir klar include
ist ein Query-Paramter, weil es mit
Fragezeichen erklärt wurde. Mehrere Query-Parameter werden mit Komma getrennt:
### Person lesen [GET /persons/{id}{?include,version,format}]
An dieser STelle könnten wir natürlich auch etwas mehr Infos zu unseren Parametern dokumentieren. Lassen Sie uns das tun indem sie Typ-Informationen angeben.
+ Parameters
+ id: 123 (string, required)
+ include: metadata (string, optional)
+ format: xml (string, optional)
Apiary zeigt das auch recht schön an:
Aber hier hören wir jetzt auch noch nicht auf. Wir möchten möglicherweise nur drei verschiedene Formate wie XML, JSON und HTML (meh…) unterstützen, und der Default sollte JSON sein. Legen wir das fest:
+ Parameters
+ format: xml (string, optional)
+ Members
+ xml
+ json
+ html
+ Default: xml
Um es kurz zu halten habe ich die anderen Parameter weggelassen. “Format” hat nun auch ein Beispiel und wir wissen auch, wir wollen einen String und der Wert ist optional.
Wir kennen jetzt auch die erlaubten Werte mittels der “Members”-Liste. Und natürlich kennen wir jetzt auch den Default-Wert.
Wieder überzeugt die Apiary-Unterstützung:
Wir haben festgestellt, wie einfach es ist mittels Blueprint und Apiary API-Resourcen zu definieren und auch zu visualisieren. Man wird natürlich auch einige Male entdecken, dass Apiary nicht unbedingt perfekt ist. In 95% aller Fälle klappt es aber recht gut.
Die Blueprint Spezifikation unterstützt URL Templates, kann Resourcen in Collections gruppieren und erlaubt ausserdem zu spezifizieren, welche Parameter existieren und notwendig sind.
Diese Spezifikation können Sie in Apiary ansehen.
Source Code für diese API finden Sie auf GitHub.
Im nächsten Post sprechen wir über MSON, was Ihnen hilft, das reine JSON zu entfernen und Objekt-Definitionen wiederzuverwenden.
Tags: #apiary #blueprint #rest #specification #documentation