OpenAPI 3.0 – Blaupause für REST APIs

Als Backend Entwickler hat man zwangsläufig mit REST APIs zu tun, doch auch im Frontend- und App-Development kommt man um die Kommunikation mit solchen APIs nicht herum. Damit die Interface-Landschaft nicht zum Wilden Westen wird und sich unterschiedliche Entwickler leichter austauschen und an einem Strang ziehen können, wird natürlich ein Standard entwickelt: OpenAPI als sprichwörtlicher Messias (bitte verzeiht den Frevel).

Beispiel OpenAPI Definition in Swagger UI

Bild zusammengestellt mit Material von OpenAPI Initiative und Swagger

Representational State Transfer (REST) ist ein Entwicklungsmuster im Bereich von Web/Netzwerk-basierten Softwaresystemen. Es legt fest, wie unterschiedliche Systeme miteinander kommunizieren und Daten austauschen. Dieser Austausch geschieht über Programmier-Schnittstellen – Application Programming Interfaces (APIs). Die OpenAPI Specification (OAS) dient der Beschreibung solcher Interfaces. Setzt eine API nun auf REST als Architektur-Vorgabe, wird daraus eine RESTful API, auch REST API genannt. Die unterschiedlichen Client-Apps für Smartphones, Desktops etc. können so alle auf eine gemeinsame Schnittstelle zugreifen und sprechen so auch die gleiche Sprache. Für gewöhnlich werden Daten über solche REST APIs im JavaScript Object Notation (JSON) Format ausgetauscht, was aber nicht zwingend notwendig ist.

A RESTful API (done right) is just a website for clients with a limited vocabulary. [Roy Fielding, InfoQ]

One Ring Specification to rule them all

Diese APIs müssen nicht nur implementiert, sondern auch dokumentiert werden, damit Mensch und Maschine gleichermaßen damit arbeiten können. Dokumentation ist in der Programmierung und Entwicklung ein sehr wichtiger Prozess, der leider oft ermüdend ist und in einem Mehr (also zusätzlich) an Arbeit resultiert. Die OpenAPI Specification (OAS) schickt sich an, diesen Aufwand zu reduzieren und eine einheitliche API-Definition zu ermöglichen. Diese Definition kann dann beispielsweise von Tools zur Generierung von Dokumentationen und Implementierungen (Client und Server), sowie für API-Tests verwendet werden.

OpenAPI hieß früher Swagger (heute OpenAPI 2.0) und die Dokumentation dazu ist auch heute noch unter swagger.io im Web zu finden. OpenAPI 3.0.2 ist die die aktuelle Version der Spezifikation zum Zeitpunkt dieses Blogposts. In einer OpenAPI-Datei (JSON oder YAML) können ganze APIs beschrieben werden, dazu zählen:

  • – Allgemeine Informationen wie Lizenz, Nutzungsbedingungen, Kontakt und Server
  • – Authentifizierungsmethoden (z.B. API-Keys oder Basic Auth)
  • – verfügbare Endpunkte (z.B. /posts) und deren HTTP-Operationen (GET, POST usw.)
  • – Eingabe- und Ausgabe-Daten für die einzelnen Operationen

Zum Tooling rund um OpenAPI gibt es eine praktische Sammlung unter openapi.tools. Dort findet sich eine Menge an Tools für OAS: Converter, um andere API-Spezifikationen zu OpenAPI 3.0 umzuwandeln, Prüftools zur Verifikation der OpenAPI Spezifikation und der eigenen API, Generatoren für schön aufbereitete Dokumentation, Editoren und Linter zur einfacheren Bearbeitung von OpenAPI-Dateien, Generatoren für Clients und Server und vieles mehr. Zum Beispiel kann man mit dem Swagger Editor OpenAPI-Dateien bearbeiten und erhält eine Live-Vorschau der Dokumentation mittels Swagger UI. Swagger Codegen liefert auch noch die Möglichkeit, sich gleich passende Client-Bibliotheken generieren zu lassen.

Ans Eingemachte (Beispiel)

Eine API-Definition nach OAS in YAML (YAML Ain’t Markup Language) sieht wie folgt aus:

openapi: 3.0.0
info:
  title: Sample API
  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/)
    or HTML.
  version: 0.1.9
servers:
- url: http://localhost:3000
  description: Optional server description, e.g. Main (production) server
paths:
  /users:
    get:
      summary: Returns a list of users.
      description: Optional extended description in CommonMark or HTML.
      x-swagger-router-controller: Users
      operationId: usersGET
      responses:
        200:
          description: A JSON array of user names
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string

Beispiel von swagger.io

Anfangs werden allgemeine Informationen zur API beschrieben, während unter paths die einzelnen Endpunkte definiert sind. Diese Datei kann man mit anderen Entwicklern teilen, damit diese eine Vorstellung von der API bekommen und ihre Software-Teile umsetzen können. Gleichzeitig kann man damit aber auch die vorher erwähnten Tools einsetzen, zum Beispiel Swagger UI.

Beispiel OpenAPI Definition in Swagger UI

Beispiel OpenAPI Definition in Swagger UI

Mit der Definition können wir auch eine Node.js Server-Applikation füttern. Alles was wir hierfür tun müssen, ist ein Skript index.js mit folgendem Inhalt:

'use strict';

var fs = require('fs'),
    path = require('path'),
    http = require('http');

var express = require('express');
var app = express();
var oasTools = require('oas-tools');
var jsyaml = require('js-yaml');
var serverPort = 3000;

// swaggerRouter configuration
var options = {
  controllers: path.join(__dirname, './controllers'),
};

// The Swagger document (require it, build it programmatically, fetch it from a URL, ...)
var spec = fs.readFileSync(path.join(__dirname,'api/swagger.yaml'), 'utf8');
var swaggerDoc = jsyaml.safeLoad(spec);

// Initialize the Swagger middleware
oasTools.initializeMiddleware(swaggerDoc, app, function (middleware) {

  // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
  app.use(middleware.swaggerMetadata());

  // Validate Swagger requests
  app.use(middleware.swaggerValidator());

  // Route validated requests to appropriate controller
  app.use(middleware.swaggerRouter(options));

  // Serve the Swagger documents and Swagger UI
  app.use(middleware.swaggerUi());

  // Start the server
  http.createServer(app).listen(serverPort, function () {
    console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort);
    console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort);
  });

});

Code aus Swagger Codegen mit Änderungen für oas-tools

Im Verzeichnis controllers/ legen wir nun die einzelnen Handler für die jeweiligen Endpunkte und Operationen ab, während in api/swagger.yaml unsere OpenAPI Definition verweilt.

Ein Handler bzw. Controller für den /users-Endpunkt könnte so aussehen:

'use strict';

const DB = require('../lib/Database');

module.exports.usersGET = async (req, res, next) => {
    const users = await DB.usersGET();
    res.send(users);
  };

Die operationId aus der API-Definition wird hier automatisch der entsprechend benannten Methode zugeordnet – nice! Die Controller-Datei ist mit x-swagger-router-controller festgelegt (controllers/Users.js). Nach der Installation der benötigten Pakete express, oas-tools und js-yaml (z.B. über NPM) ist die Applikation dann auch schon lauffähig und unter http://localhost:3000 bzw. http://localhost:3000/docs (Swagger UI) erreichbar.

Klingt gut, schmeckt gut

Was heißt das konkret? App-Entwickler müssen dank fertiger Spezifikation und Mocking-Möglichkeiten nicht auf ein fertiges Backend warten, ehe sie mit der Entwicklung und dem Testing beginnen können. Backend-Entwickler können die Validierung der Parameter an die entsprechenden OAS Tools abgeben. Und und und. Wie ihr sehen könnt, bietet OpenAPI einige Vorteile für die koordinierte Entwicklung von Schnittstellen in der Welt der REST APIs und, wer weiß, vielleicht werden in Zukunft weniger Bücher wie Surviving Other People’s APIs verfasst.  😉