APIs sind aus der heutigen digitalen Wirtschaft nicht mehr wegzudenken. Schon 2019 haben API-Aufrufe 83 Prozent des Internet-Traffics ausgemacht, größtenteils von individuellen Anwendungen in der Cloud – ganz klar eine Folge digitaler Transformation. Es ist in keiner Weise von einem rückläufigen Trend auszugehen. Im Gegenteil, die API-Economy entwickelt sich weiter rasant und wir können davon ausgehen, dass APIs zu den Schlüsselfaktoren für den Erfolg digitaler Geschäftsmodelle zählen. Durch die Möglichkeit zur Bereitstellung von Daten und Funktionen sind APIs Wegbereiter digitaler Öffnung. Wer sich öffnet, bekommt die Chance auf Bildung eines Ökosystems oder einer Plattform:
APIs erlauben es, ein umfassendes Ökosystem zu schaffen, in dem verschiedene Partner, Entwicklerinnen und Entwickler sowie Drittanbieter eingebunden und ermutigt werden, beispielsweise eigene Anwendungen und Dienste zu beizusteuern. Neue Dienste können entwickelt und die Funktionalität der gesamten Plattform kann erweitert werden. Interoperabilität wird vereinfacht und es entstehen zusätzliche Einnahmequellen auf dem Weg zu Innovation und Flexibilität. Das kann auch die Einbindung fortschrittlicher Technologien wie künstlicher Intelligenz umfassen. Das vielleicht beste Beispiel dafür sind die zahlreichen Dienste, die rund um die APIs von OpenAI entstehen.
Es zeigt sich also, dass in geschäftlicher Hinsicht der Mehrwert nicht durch eine einzelne API allein geschaffen wird. Integration lautet das Zauberwort: Mehrere APIs werden miteinander verbunden und koordiniert, um ein nahtloses und effizientes System zu schaffen, in dem Daten und Funktionen über Systemgrenzen hinweg genutzt werden können.
Nahtlos? Wie geht denn das? Wie kann ich das darstellen und beschreiben? Bis vor kurzem war diese Frage tatsächlich gar nicht so einfach und ohne Weiteres zu beantworten.
Für eine individuelle API liegt die Sache absolut klar: Mit OpenAPI (früher bekannt als Swagger) haben wir eine standardisierte Spezifikation für die Beschreibung von Web APIs zur Verfügung. In menschen- und maschinenlesbarer Form können wir beschreiben wie die API funktioniert, welche Endpunkte existieren, welche Parameter benötigt und welche Datenformate verwendet werden. Mit anderen Worten: OpenAPI macht es uns möglich, genau zu beschreiben, was eine API bietet und wie sie funktioniert – in der Regel auf der Ebene einzelner Endpunkte.
Erfordert die Funktionalität das Ineinandergreifen mehrerer Endpunkte, um geschäftlichen Mehrwert zu erzielen, stoßen wir an Grenzen. Wir müssen einen Workflow abbilden, können das mit OpenAPI allein jedoch nur, indem wir die Beschreibung der einzelnen Endpunkte mit Prosa anreichern, so dass ein dazugehöriger Ablauf nachvollziehbar wird.
Beispiel: Datei-Upload und Verarbeitung
Schauen wir uns das anhand eines einfachen Beispiels an.
Wir stellen uns vor, wir möchten irgendeine Datei hochladen, um diese später weiterverarbeiten zu lassen – sei es für eine Bildkomprimierung, die Texterkennung in einem Scan oder das Konvertieren eines Formats. Dieser Prozess lässt sich mit APIs einfach automatisieren und elegant orchestrieren.
Im Folgenden schauen wir uns Schritt für Schritt an, wie ein solcher Workflow spezifiziert und umgesetzt werden kann. Er umfasst die folgenden Schritte:
- Datei hochladen
- Datei verarbeiten
- Verarbeitungsstatus abrufen
Eine einfache OpenAPI-Spezifikation für eine API mit der beschriebenen Funktionalität könnte im YAML-Format beispielsweise so aussehen:
1openapi: 3.0.0
2info:
3 title: Datei-Verarbeitungs-API
4 version: 1.0.0
5 description: |
6 Diese API ermöglicht die Automatisierung eines Workflows
7 für den Datei-Upload, die Verarbeitung und die Statusabfrage.
8 Ein typischer Workflow umfasst folgende Schritte:
9
10 1. **Datei hochladen**: Eine Datei wird über den `/upload`-Endpunkt
11 hochgeladen. Die API gibt eine eindeutige Datei-ID zurück.
12 2. **Datei verarbeiten**: Die hochgeladene Datei wird über den
13 `/process`-Endpunkt verarbeitet. Der Verarbeitungsstatus wird zurückgegeben.
14 3. **Verarbeitungsstatus abrufen**: Mit dem `/status/{fileId}`-Endpunkt
15 kann der aktuelle Status der Datei abgefragt werden.
16
17 Beispiel-Workflow:
18 - Eine Datei wird hochgeladen.
19 - Der Workflow wartet, bis die Datei erfolgreich verarbeitet wurde.
20 - Am Ende wird eine Bestätigung über den Abschluss des Prozesses
21 zurückgegeben.
22
23paths:
24 /upload:
25 post:
26 summary: Datei hochladen
27 description: |
28 Lädt eine Datei hoch und gibt eine eindeutige Datei-ID
29 zurück, die für die weiteren Schritte verwendet wird.
30 Der erste Schritt im Workflow ist der Datei-Upload.
31 Diese Datei-ID ist notwendig für die Verarbeitung und Statusabfrage.
32 operationId: uploadFile
33 requestBody:
34 required: true
35 content:
36 multipart/form-data:
37 schema:
38 type: object
39 properties:
40 file:
41 type: string
42 format: binary
43 description: Die Datei, die hochgeladen werden soll.
44 responses:
45 '200':
46 description: Datei erfolgreich hochgeladen.
47 content:
48 application/json:
49 schema:
50 type: object
51 properties:
52 fileId:
53 type: string
54 description: Eindeutige ID, die die
55 hochgeladene Datei identifiziert.
56 example:
57 fileId: "abc123"
58 '400':
59 description: Ungültige Anfrage, z. B. fehlende Datei.
60 /process:
61 post:
62 summary: Datei verarbeiten
63 description: |
64 Startet die Verarbeitung einer hochgeladenen Datei
65 anhand ihrer ID.
66 Dieser Schritt im Workflow wird ausgelöst, sobald der
67 Upload abgeschlossen ist. Die Verarbeitung erfolgt asynchron,
68 und der Verarbeitungsstatus kann später über den `/status`-Endpunkt abgefragt werden.
69 operationId: processFile
70 requestBody:
71 required: true
72 content:
73 application/json:
74 schema:
75 type: object
76 properties:
77 fileId:
78 type: string
79 description: Die ID der zu verarbeitenden Datei.
80 example:
81 fileId: "abc123"
82 responses:
83 '200':
84 description: Datei erfolgreich zur Verarbeitung eingereicht.
85 content:
86 application/json:
87 schema:
88 type: object
89 properties:
90 status:
91 type: string
92 enum: [processing, completed, failed]
93 description: Status der Verarbeitung.
94 example:
95 status: "processing"
96 '400':
97 description: Ungültige Anfrage, z. B. fehlende Datei-ID.
98 /status/{fileId}:
99 get:
100 summary: Verarbeitungsstatus abrufen
101 description: |
102 Gibt den aktuellen Status der Verarbeitung einer Datei zurück.
103 Dieser Schritt im Workflow wird verwendet, um den Abschluss des
104 Verarbeitungsprozesses zu prüfen. Sobald der Status "completed"
105 erreicht ist, ist die Datei vollständig verarbeitet.
106 operationId: getProcessingStatus
107 parameters:
108 - name: fileId
109 in: path
110 required: true
111 schema:
112 type: string
113 description: Die ID der Datei, deren Verarbeitungsstatus
114 abgefragt werden soll.
115 responses:
116 '200':
117 description: Der aktuelle Verarbeitungsstatus der Datei.
118 content:
119 application/json:
120 schema:
121 type: object
122 properties:
123 status:
124 type: string
125 enum: [processing, completed, failed]
126 description: Der aktuelle Status der Datei.
127 example:
128 status: "completed"
129 '404':
130 description: Datei nicht gefunden.
131 content:
132 application/json:
133 schema:
134 type: object
135 properties:
136 error:
137 type: string
138 description: Fehlerbeschreibung.
139 example:
140 error: "Datei nicht gefunden."
OpenAPI für die Beschreibung von API-Workflows?
Obwohl OpenAPI grundsätzlich maschinenlesbar ist – in manchen Fällen sogar von Maschinen besser gelesen werden kann als von Menschen – stellen wir fest, dass das Beispiel oben eigentlich nur noch (bestenfalls) menschenlesbar ist. Die Informationen zur Abfolge von Requests können wir der Dokumentation zwar entnehmen, jedoch ist die Beschreibung des Workflows in keiner Weise standardisiert. Und damit ist diese Spezifikation für die Beschreibung des Workflows oder für die Dokumentation eines Integrationsszenarios nur eingeschränkt hilfreich.
Und stellen wir uns dann vor, dass unsere API nicht nur genau die Endpunkte enthält, die wir für unseren kleinen Beispiel-Workflow benötigen, sondern stattdessen eine Vielzahl von Endpunkten für die unterschiedlichsten Workflows, stößt diese Art der Beschreibung sofort an ihre Grenzen und ist ganz schnell auch für Menschen nicht mehr hilfreich.
Arazzo für die Beschreibung von API-Workflows!
An dieser Stelle kommt Arazzo als neue Spezifikation der OpenAPI Initiative für die Beschreibung von API-Workflows ins Spiel. Das Ziel des neuen Standards ist die maschinenlesbare Beschreibung von API-Workflows. Sie orientieren sich an Use Cases und sind somit eine Reihe von API-Aufrufen, die gemeinsam Mehrwert schaffen und Geschäftszielen dienen.
Mit anderen Worten: Mit Arazzo spezifizierte Workflows sind deterministische Rezepte für die Verwendung von APIs. Wir können genau ausdrücken, wie sie verwendet werden sollen. Diese Art der expliziten Informationen kommt früher oder später auch diversen KI-Tools zu Gute. Die exakte Beschreibung von notwendigen Schritten, die für einen Anwendungsfall aus einem Geschäftskontext erforderlich sind, kann von künstlicher Intelligenz abgearbeitet werden und dadurch den Mehrwert des geschäftlichen Anwendungsfalls bieten.
Oder wir können uns von Code-Generierung auf Grundlage von OpenAPI-Spezifikationen verabschieden. Bisher waren die Ergebnisse generierte Clients, die die Operationen aus der API-Dokumentation 1:1 bereitgestellt haben. Wie oder in welcher Reihenfolge die Operationen dann aber zu verwenden waren, blieb unklar und musste händisch implementiert werden. Mit entsprechendem Tool-Support kann auch hier die Arazzo-Workflow-Spezifikation ihren Beitrag leisten und gezielte Code-Generierung möglich machen. Die im generierten SDK enthaltene Funktion ist dann nicht mehr uploadFile()
sondern kann executeFileProcessingWorkflow()
sein. Die Interaktion mit der API ist direkt am durch die API gebotenen Mehrwert ausgerichtet und macht eine neue Abstraktionsebene spezifizierbar.
Beispiel: Datei-Verarbeitung als Arazzo-Workflow
Kommen wir zurück zum Beispiel des Datei-Uploads mit anschließender Verarbeitung. Im Folgenden werden wir den bereits umrissenen Workflow mit Hilfe von Arazzo spezifizieren.
Dabei stellen wir ganz klar fest, dass die Spezifikation die Handschrift der OpenAPI-Initiative trägt und vertraute Konzepte verwendet. Das senkt die Einstiegshürde für die Arbeit mit der API-Workflow-Spezifikation.
Schritt 1: Definition der Arazzo-Spezifikation
Beginnen wir mit der Definition der grundlegenden Metadaten des Workflows. Diese umfasst den Namen des Workflows, eine Beschreibung und die Version. Genau wie eine OpenAPI-Spezifikation beginnt eine Arazzo-Spezifikation mit einem info
-Block für Metainformationen zum Workflow. Die Felder title
und version
müssen dabei angegeben werden.
Anschließend folgt eine Liste von API-Beschreibungen (sourceDescriptions
), die für den Workflow erforderlich sind. In unserem Beispiel ist das nur eine OpenAPI-Spezifikation:
1arazzo: '1.0.0'
2info:
3 title: Datei-Verarbeitungs-Workflow
4 version: '1.0.0'
5 description: |
6 Dieser Workflow automatisiert den Prozess des Hochladens, Verarbeitens und
7 Überprüfens des Status einer Datei.
8sourceDescriptions:
9 - name: fileProcessingAPI
10 url: https://example.com/openapi.yaml
11 type: openapi
Schritt 2: Grundlegende Definition des Workflows
Im nächsten Abschnitt folgt dann die Beschreibung der Workflows. Ja, eine Arazzo-Spezifikation kann durchaus auch gleich mehrere Workflows basierend auf gemeinsamen APIs enthalten. Es muss also nicht für jeden Workflow eine eigene Spezifikation erstellt werden. Vor diesem Hintergrund zahlen sich auch die bereits aus OpenAPI bekannten, wiederverwendbaren components
aus.
In der Arazzo-Workflow-Spezifikation ist ein Workflow eine strukturierte Beschreibung einer Abfolge von Schritten, die automatisiert ausgeführt werden. Ein Workflow wird innerhalb des workflows
-Objekts definiert, das folgende zentrale Bestandteile hat:
workflowId
: Ein eindeutiger Bezeichner für den Workflow.summary
: Eine kurze Zusammenfassung des Workflows.description
: Eine ausführliche Beschreibung des Workflows, die auch den Kontext und Zweck erläutert.inputs
: Die Eingabedaten, die der Workflow benötigt, um ausgeführt zu werden.steps
: Eine Liste von Schritten, die in einer definierten Reihenfolge ausgeführt werden.
Die Spezifikation eines Workflows beginnt also mit einer ID, einer Zusammenfassung und Beschreibung:
1workflows:
2 - workflowId: fileUploadProcessing
3 summary: Automatisiert den Datei-Upload und die Verarbeitung.
4 description: |
5 Dieser Workflow lädt eine Datei hoch, startet deren Verarbeitung und
6 überprüft den Abschluss der Verarbeitung.
Schritt 3: Definieren der Eingabe-Parameter
Ein Workflow benötigt bestimmte Parameter, um ausgeführt zu werden. Im Beispiel ist dies die Datei, die verarbeitet werden soll. Diese Parameter werden als inputs
definiert:
1inputs:
2 type: object
3 properties:
4 file:
5 type: string
6 format: binary
7 description: Die Datei, die hochgeladen und verarbeitet werden soll.
Schritt 4: Hinzufügen der Schritte
Anschließend folgen die einzelnen Schritte des Workflows: Die einzelnen Aktionen, die der Workflow ausführt, werden als Schritte innerhalb des steps
-Objekts definiert. Jeder Schritt hat eine eindeutige stepId
, eine Beschreibung und Verweise auf die entsprechenden API-Endpunkte.
Jeder Schritt ist dabei ebenfalls über eine ID eindeutig identifizierbar und referenziert gleichzeitig eine Operation einer API.
Schritt 4.1: Datei hochladen
Die operationId
stellt zusammen mit dem parameters
-Objekt den Bezug zur API-Spezifikation her, wobei der Wert von values
im folgenden Beispiel auf die definierten Eingabe-Parameter des Workflows zurückgreift:
1steps:
2 - stepId: uploadFile
3 description: Lädt die Datei hoch und erhält eine eindeutige Datei-ID.
4 operationId: uploadFile
5 parameters:
6 - name: file
7 in: body
8 value: $inputs.file
9 successCriteria:
10 - condition: $statusCode == 200
11 outputs:
12 fileId: $response.body.fileId
successCriteria
definiert dann die Bedingungen, unter denen der Schritt als erfolgreich abgeschlossen gilt. Und outputs
sammelt relelevante Ausgaben, die für folgende Schritte benötigt werden. Im Beispiel ist das das Feld fileId
aus der Antwort des API-Aufrufs für das Hochladen der Datei.
Schritt 4.2: Datei verarbeiten
Der Output fileId
aus dem ersten Schritt wird im Folgenden als Eingabe-Parameter im Schritt für die Verarbeitung der hochgeladenen Datei verwendet:
1- stepId: processFile
2 description: Startet die Verarbeitung der hochgeladenen Datei.
3 operationId: processFile
4 parameters:
5 - name: fileId
6 in: body
7 value: $steps.uploadFile.outputs.fileId
8 successCriteria:
9 - condition: $statusCode == 200
10 outputs:
11 status: $response.body.status
In diesem Schritt wird das Feld status
aus der Antwort als Ausgabe definiert.
Schritt 4.3: Verarbeitungsstatus prüfen
Nachdem im vorangegangenen Schritt die Verarbeitung der Datei gestartet wurde, wird im nächsten Schritt der Status der (asynchron ablaufenden) Verarbeitung geprüft.
Im einfachsten Fall kann der Schritt dafür wie folgt definiert werden:
1- stepId: checkStatus
2 description: Überprüft den aktuellen Verarbeitungsstatus der Datei.
3 operationId: getProcessingStatus
4 parameters:
5 - name: fileId
6 in: path
7 value: $steps.uploadFile.outputs.fileId
8 successCriteria:
9 - condition: $statusCode == 200
10 - condition: $response.body.status == 'completed'
11 outputs:
12 finalStatus: $response.body.status
Allerdings müssen wir davon ausgehen, dass die Verarbeitung nicht sofort erfolgreich abgeschlossen ist, d.h. status
in der ersten Antwort bereits auf completed
gesetzt ist, wie es die successCriteria
vorsehen.
Das bedeutet, wir müssen wiederholte Abfragen vorsehen. Dafür können wir die Definition des Schritts checkStatus
anpassen, indem wir mittels onFailure
einen Retry einbauen:
1- stepId: checkStatus
2 description: Überprüft den aktuellen Verarbeitungsstatus der Datei.
3 operationId: getProcessingStatus
4 parameters:
5 - name: fileId
6 in: path
7 value: $steps.uploadFile.outputs.fileId
8 successCriteria:
9 - condition: $statusCode == 200
10 - condition: $.outputs.status == 'completed'
11 outputs:
12 status: $response.body.status
13 onFailure:
14 - name: retryCheckStatus
15 type: retry
16 stepId: checkStatus
17 retryAfter: 5
18 retryLimit: 10
19 criteria:
20 - condition: $.outputs.status == 'processing'
21 - name: terminateWorkflow
22 type: end
23 criteria:
24 - condition: $.outputs.status == 'failed'
Sofern die Abfrage des Verarbeitungsstatus der Datei nicht mit dem HTTP-Status 200 OK beantwortet wird und das Feld status
in der Antwort nicht auf completed
gesetzt ist, wird der Schritt nach 5 Sekunden wiederholt (höchstens zehnmal), sofern status
noch auf processing
gesetzt ist. Im Fall von failed
wird der Workflow abgebrochen.
Vollständige Spezifikation des Workflows
Hier ist die vollständige Workflow-Spezifikation:
1arazzo: '1.0.0'
2info:
3 title: Datei-Verarbeitungs-Workflow
4 version: '1.0.0'
5 description: |
6 Dieser Workflow automatisiert den Prozess des Hochladens,
7 Verarbeitens und Überprüfens des Status einer Datei.
8sourceDescriptions:
9 - name: fileProcessingAPI
10 url: https://example.com/openapi.yaml
11 type: openapi
12workflows:
13 - workflowId: fileUploadProcessing
14 summary: Automatisiert den Datei-Upload und die Verarbeitung.
15 description: |
16 Dieser Workflow lädt eine Datei hoch, startet deren Verarbeitung
17 und überprüft den Abschluss der Verarbeitung, indem die Statusprüfung
18 mehrfach wiederholt wird.
19 inputs:
20 type: object
21 properties:
22 file:
23 type: string
24 format: binary
25 description: Die Datei, die hochgeladen und verarbeitet werden soll.
26 steps:
27 - stepId: uploadFile
28 description: Lädt die Datei hoch und erhält eine eindeutige Datei-ID.
29 operationId: uploadFile
30 parameters:
31 - name: file
32 in: body
33 value: $inputs.file
34 successCriteria:
35 - condition: $statusCode == 200
36 outputs:
37 fileId: $response.body.fileId
38
39 - stepId: processFile
40 description: Startet die Verarbeitung der hochgeladenen Datei.
41 operationId: processFile
42 parameters:
43 - name: fileId
44 in: body
45 value: $steps.uploadFile.outputs.fileId
46 successCriteria:
47 - condition: $statusCode == 200
48 outputs:
49 status: $response.body.status
50
51 - stepId: checkStatus
52 description: Überprüft den aktuellen Verarbeitungsstatus der Datei.
53 operationId: getProcessingStatus
54 parameters:
55 - name: fileId
56 in: path
57 value: $steps.uploadFile.outputs.fileId
58 successCriteria:
59 - condition: $statusCode == 200
60 - condition: $.outputs.status == 'completed'
61 outputs:
62 status: $response.body.status
63 onFailure:
64 - name: retryCheckStatus
65 type: retry
66 stepId: checkStatus
67 retryAfter: 5
68 retryLimit: 10
69 criteria:
70 - condition: $.outputs.status == 'pending'
71 - name: terminateWorkflow
72 type: end
73 criteria:
74 - condition: $.outputs.status == 'failed'
Möglichkeiten zur Beschreibung von Workflow-Logik
Das Beispiel hat gezeigt, dass Arazzo uns mit onSuccess
und onFailure
in der Definition von Schritten einfache Möglichkeiten zum Abbilden von Logik innerhalb des Workflows bietet. Neben retry
sind mit goto
auch Verzweigungen zu anderen Schritten oder sogar anderen Workflows möglich.
Genauso können in komplexeren Szenarien auch Abhängigkeiten zwischen verschiedenen Workflows definiert werden: Das Attribut dependsOn
einer Workflow-Beschreibung in workflows
erlaubt es, Workflows anzugeben, die abgeschlossen sein müssen, bevor der entsprechende Workflow ausgeführt werden kann. Entsprechend können sich Parameter-Werte auf andere Workflows beziehen.
Die Fähigkeit, Workflows zu verketten und aufeinander abzustimmen, macht die Spezifikation besonders mächtig und bietet eine klare Struktur für Automatisierungsprozesse und Integrationsszenarien. Diese deterministische Beschreibung kann uns auch beim Testen unserer APIs zugute kommen, da Abhängigkeiten und Verzweigungen explizit definiert sind.
Ausblick
Bisher ist die Arazzo-Spezifikation noch sehr neu. Dadurch sind natürlich noch nicht alle Wünsche erfüllt oder alle denkbaren Szenarien abgebildet. Beispielsweise hätte es die Unterstützung für andere APIs als in OpenAPI spezifierte synchrone HTTP-APIs fast in die Version 1.0.0 des Arazzo-Standards geschafft. Das ist dann jedoch zugunsten einer stabilen ersten Version vorerst verworfen worden. Eventgetriebene APIs werden in einem nächsten Schritt nichtsdestotrotz ihren Weg in die Spezifikation des Standards finden.
Ziel ist auf jeden Fall eine große Verbreitung von Arazzo für die Beschreibung von API-Workflows zu erreichen. Die Relevanz ist da, das Interesse auch.
Um den Standard so richtig zum Leben zu erwecken, ist natürlich entsprechender Tooling-Support hilfreich, wenn nicht sogar erforderlich. Mit Blick auf die breite Unterstützung der verwandten OpenAPI-Spezifikation ist davon auszugehen, dass kompatible Tools nicht allzu lange auf sich warten lassen werden.
Mit Redocly ist bereits ein Linter für Arazzo verfügbar, sowohl als Tool für die Kommandozeile als auch als Extension in Visual Studio Code. Damit sind die ersten Weichen für die Adoption des neuen Standards gestellt, spezifikationskonforme Workflows können erstellt werden und weitere Tools werden folgen. Die Redocly-Roadmap gibt bereits einen Vorgeschmack darauf.
Wer jetzt auf den Geschmack gekommen ist, findet weitere Informationen sowie eine Sammlung von Beispielen im Arazzo-Repository der OpenAPI-Initiative. Kommt auch gern auf uns zu, um gemeinsam die Möglichkeiten zu entdecken, die sich mit API-Workflows und Arazzo als offener standardisierter Möglichkeit zur Definition ergeben!