Fast jeder Entwickler der sich im Web bewegt, wird derzeit früher oder später Kontakt mit der OpenAPI Initiative machen. Ziel ist es die Spezifikation von (primär REST)-Schnittstellen zu vereinheitlichen und dabei eine Version zu erzeugen die auch maschinenlesbar ist. Damit eröffnen sich viele Möglichkeiten zur Validierung, Dokumentation und wo nötig auch Code-Generierung wenn man entsprechende Frameworks einsetzt und externe Rest-APIs anspricht. Soweit so gut.
Die Spezifikation ist mittlerweile recht umfangreich und beschreibt sehr viele der Use-Cases recht gut. Insbesondere die Methodik zur Beschreibung von Endpunkten ist in meinen Augen wirklich gut gelungen. Nun gilt aber wie immer: Wo Licht ist, ist auch Schatten. Gerade im Umfeld der Schema-Definitionen bin ich auf eine Lästigkeit gestoßen, für die ich noch keine wirklich brauchbare Lösung gefunden habe. Im Wesentlichen geht es dabei um dynamische indizierte Arrays bzw. Objekte.
Im Folgenden will ich ein Beispiel nutzen, das die Problematik illustriert: Für eine Basis-Entität existiert ein Verweis an eine Anzahl 0…n weiterer Entitäten. Als Beispiel nehmen wir eine Übersetzung in verschiedene Landessprachen, dabei ist nicht vorab bekannt welche Sprache bzw. Locale tatsächlich verfügbar ist (z.B. weil die Übersetzungen in einem getrennten Zyklus erstellt werden und noch nicht verfügbar sind). Wir machen also ein einfaches Objekt das wir in JSON wie folgt beschreiben:
{ "id" : 4711, "vendor": "ACME" "name" : { "de" : "in deutsch", "fr" : "en francais", "en" : "in english" } "images" : [ { "image_id" : 23, "type" : "front_view", "image_relation" : "von vorne"}, { "image_id" : 24, "type" : "unboxing", image_relation" : "ausgepackt"} ] }
Das Problem mit der Spezifikation ist nun, dass man den Anteil unter Name nicht vorab definieren kann, man weiß nicht ob zum Beispiel eine Übersetzung für Spanisch vorhanden ist. Gleiches gilt für die Bilder – je nachdem wie viele Bilder vorhanden sind, enthält das Array eine beliebige Anzahl Referenzen. Im Beispiel sind es zwei.
Im Code-Beispiel habe ich auch gleich zwei verschiedene Varianten gewählt in denen man eine derartige Relation in JSON abbilden kann. Da gibt es prinzipiell kein richtig und kein falsch. Allerdings gibt es doch erhebliche Unterschiede wenn man an die Weiterverarbeitung denkt oder kommt, dazu gleich mehr.
Betrachten wir erst einmal JavaScript bzw. JSON – das ist recht dynamisch, es gibt für JSON keine Art „vorgeschaltete“ Klassendefinition wie z.B. in SOAP. Aus dem Bedarf heraus die Beschreibung von JSON-Antworten zu vereinheitlichen entstand die OpenAPI Initiative. In dynamisch typisierten Sprachen wie PHP und JavaScript ist das auch kein Problem, sondern von Haus aus direkt mit dabei: Man kann aus einem validen JSON-String entsprechende Objekte oder arrays erzeugen und damit machen was immer gerade notwendig ist. In anderen Sprachen kann hier etwas mehr Aufwand notwendig sein, PHP bietet vor allem die Klasse „stdClass“ ein gutes Ausgangskonstrukt, was sich auch darin niederschlägt, dass es der default Rückgabewert von „json_decode“ ist.
Die für die Übersetzung des Namens gezeigte Variante macht es sehr einfach die gerade benötigte Übersetzung aus dem Array bzw. Objekt zu holen (oder auch nur zu prüfen ob diese vorhanden ist: Einfach schauen ob der Index gesetzt ist und schon kann man damit arbeiten. Anders sieht es aus wenn man die Bilder betrachtet, zum Beispiel um zu wissen ob eine Frontansicht existiert: Man muss hier über das gesamte Array iterieren und sich merken/wegschreiben/abspringen wenn man ein passendes Bild gefunden hat, genauso wie den Fall, dass es den Eintrag nicht geben sollte.
Die Spezifikation von OpenAPI lässt es zwar zu mittels „additional“ properties dynamische Werte zu definieren und ggf. auch deren Typ, aber eine feste Aussage wie das ist ein Array welches anhand eines Unique-Identifiers indiziert ist gibt so nicht (oder ich habe es noch immer nicht gefunden). Somit wird in meinen Augen leider viel Potential der JSON-Objekte einfach verschenkt obwohl es in der Praxis recht häufig viel helfen würde (es spricht ja nichts dagegen innerhalb des Objekts die ID nochmals zu wiederholen, falls man einen Client hat, der weniger dynamisch ist und bei dem es ggf. einfacher ist über die Werte zu iterieren.
Fazit: Dokumentation nach einem klaren Schema ist eine schöne Sache, aber wenn dabei leider viele sehr praktische Punkte auf der Strecke bleiben muss man sich fragen ob das wirklich so sinnvoll ist. Lieber eine OpenAPI-Spezifikation als keine, aber das „silver bullet“ ist auch der derzeitige Stand leider nicht.
Über das Thema, dass man bei der Dokumentation auch die fachliche Seite bzw. die Bedeutung / Eigenschaften bestimmter Attribute mit beschreiben sollte, reden wir hier mal besser nicht. OpenAPI bietet sich für derartige Dokumentation an, aber all zu häufig verlassen sich Entwickler dann doch darauf „na das ist doch klar was damit gemeint ist“, das geht so lange gut bis die erste Fehleinschätzung eines Nutzers auftritt.