Blog
Data Mocking: Möglichkeiten eines Fake-Backends
Wir möchten in diesem Beitrag zeigen, welche Vorteile ein simuliertes Backend bietet und die Frage beantworten, welchen Möglichkeiten es zur einfachen Erstellung eines Fake-Backends gibt.
In heutigen Web-Anwendungen arbeiten wir zumeist mit einer losen Kopplung von Frontend und Backend. Das macht es möglich, dass die einzelnen Teile als separate Projekte gehandhabt werden können. So kann das Backend etwa eine RESTful API bereitstellen, die völlig losgelöst von der Frontend-Technologie ist und eine andere, idealerweise semantische, Versionierung hat als das Frontend. Die Trennung bietet ferner die Flexibilität, bei Änderungen im Frontend nur dieses neu auszurollen, oder auch das Backend für ein anderes Frontend mit einem ganz anderen technologischen Stack zu nutzen – zum Beispiel native Anwendungen als Ergänzung zur einer Webanwendung.
Während der Entwicklung arbeiten in der Regel verschiedene Personen jeweils am Frontend und am Backend. Das Backend-Team muss sicherstellen, dass der Inhalt, der über die API bereitgestellt wird, korrekt ist. Währenddessen spielt die Validität des Inhalts für die Implementierung des Frontends eine untergeordnete Rolle. Das Frontend-Team kümmert sich vorrangig darum, dass Daten im korrekten Format und in der richtigen Struktur zur Verfügung stehen, um diese ggf. aufbereitet in den Views einer Applikation darzustellen.
Bei der Arbeit mit gemockten Daten sollten sich die Teams zu Beginn der Realisierung eines Features über das Datenformat (JSON) einigen. Dann kann im Frontend mit den Dummy-Daten sofort produktiv gearbeitet werden, während das Backend in Ruhe daran arbeiteten kann, die echten Daten bereitzustellen.
Für das Frontend-Team ist die Unabhängigkeit vom real existierenden Backend ein echter Vorteil. So kann es beispielsweise auch dann produktiv bleiben, wenn sich die Entwicklung des Backends aus irgendeinem Grund verzögert. Das führt uns zu der Frage:
Wie können wir das Backend auf einfache Art und Weise faken?
Michael Kühnel
Frontendentwickler
Viele Wege ein Backend zu faken
Es gibt, je nach Bedarf, verwendeten Technologien und Anforderungen eines Projekts verschiedene Möglichkeiten, Dummy-Daten für das Frontend bereitzustellen:
- Statische JSON-Dateien via Ajax,
- Online Mocking Services,
- JSON-Server,
- ein eigener Server.
Im Folgenden werden wir die Details der Möglichkeiten sowie deren Vor- und Nachteile erkunden.
1. JSON-Dateien über Ajax bereitstellen
Dies ist der einfachste Ansatz und ohne jegliches Tooling zu bewerkstelligen. Erstellen Sie einfach Ihre Dummy-Daten und speichern Sie diese im Dateisystem Ihres Frontend-Projektes.
Pro:
- Unkompliziertheit,
- Dummy-Daten in der Versionskontrolle (z. B. Git).
Die Ablage der Dateien im Dateisystem hat den großen Vorteil, dass die Dummy-Daten nur aus einer einzige Quelle stammen. Dies ist vor allem bei der Arbeit im Team wichtig, insbesondere für die Koordination zwischen Frontend- und Backend-Team.
Contra:
- Sehr begrenzte Funktionalität,
- ggf. keine Unterstützung durch das genutzte JavaScript-Framework™,
- keine Verwendung von HTTP-Methoden neben GET möglich,
- nur eingeschränkter Test von Fehlerbehandlungen möglich (außer 404-Error),
- Unterschiede zwischen dem gefakten und dem echten Backend.
Diese Art des Fakings mag für eine Webapplikation oder Webseite, die nur wenig Daten über Ajax laden muss, ein gangbarer Weg sein. Andernfalls überwiegen vermutlich die Nachteile. Der größte Nachteil ist vor allem der Unterschied zwischen dem vorgetäuschten Backend und dem realen Backend in der Produktion. Dies umfasst sowohl verschiedene URLs (einschließlich einer ganz anderen Struktur) als auch andere HTTP-Header, die so nur mangelhaft simuliert und getestet werden können.
2. Online Mocking Services
Neben der ersten Möglichkeit gibt es für das Mocking von APIs spezialisierte Online-Dienste wie mockapi.io, mocky.io und jsonstub.com. Diese bieten eine gute und einfache Alternative, die HTTP-Responses des Backends zu simulieren.
Pro:
- Einfach zu verwenden,
- gute und je nach Anbieter leistungsstarke Konfigurationsmöglichkeiten.
Die Handhabung ist fast so einfach, wie statische JSON-Dateien zu speichern. Dazu bietet diese Option weitere Möglichkeiten, Dinge wie HTTP-Statuscodes und HTTP-Methoden zu konfigurieren.
Contra:
- CORS-Einschränkungen (falls zutreffend),
- Daten liegen auf öffentlichen Servern,
- Abhängigkeit vom Anbieter,
- die Daten obliegen nicht der Versionskontrolle.
Der Abhängigkeitsfaktor ist vermutlich das größte Gegenargument. Es gibt ein ganzes Spektrum von Unsicherheiten, vor allem was die Verfügbarkeit des Services angeht. Sie sollten sich zumindest die folgenden Fragen stellen: Kann ich weiter arbeiten, wenn der Service ausfällt? Was passiert, wenn der Anbieter seinen Dienst einstellt? Werde ich meine Daten exportieren können?
Dass die Dummy-Daten nicht unter Ihrer Versionskontrolle liegen, ist ebenfalls ein großer Nachteil, vor allem wenn Sie nicht allein arbeiten. Natürlich können Sie die Dummy-Daten redundant in Ihrem Dateisystem speichern. Dies kann jedoch zu einem erhöhten Maintenance-Aufwand führen und Verwirrung stiften, weil niemand sicher ist, ob die Dateien in der Versionskontrolle aktuell sind bzw. genau denen entsprechen, die durch die Online-Service bereitgestellt werden.
3. JSON-Server
JSON-Server ist ein cleveres Kommandozeilen-Tool und sehr einfach zu bedienen. Selbst für Leute, die nicht den ganzen Tag im Terminal verbringen. Zitat vom Anbieter:
Get a full fake REST API with zero coding in less than 30 seconds!
Es wird ermöglicht, dass JSON-Dateien aus dem Dateisystem über einen lokalen Server bereitgestellt werden.
Pro:
- Die Dummy-Daten sind in Ihrer Versionskontrolle,
- einfach zu konfigurieren,
- es unterstützt die gängigsten HTTP-Methoden,
- es speichert Änderungen in Ihren JSON-Quelle für POST, PUT, PATCH oder DELETE-Requests.
Das hört sich zunächst nach einer perfekten Lösung an. Sie können Ihre Dummy-Daten als JSON-Dateien in Ihrem Projekt speichern und diese über ein externes Tool via HTTP zur Verfügung stellen. Es gibt jedoch auch ein paar Nachteile, von denen das eine oder andere je nach Projektanforderung ein Showstopper sein könnte.
Contra:
- Der Payload für HTTP-Requests muss zwingend im JSON-Format übermittelt werden,
- keine Routen mit selbst definierten dynamischen Teilen möglich,
- api/articles/{offset}/{count},
- Wartbarkeit nur einer einzigen JSON-Datei für alle Endpunkte bei größeren Projekten.
Es ist einfach zu bedienen, aber das bedeutet leider auch, dass einige der Konventionen Ihnen in die Quere kommen können.
Die Payload-Einschränkung sollte kein Problem sein, vorausgesetzt, Sie verfügen auch über die volle Kontrolle im echten Backend. Problematisch wird es allerdings, wenn z. B. Ihr reales Backend nur eine Integer-Zahl akzeptiert, um etwa einen Datensatz zu löschen.
Der JSON-Server bietet URL-Parameter für Paginierung. Aber diese sind an ein festes URL-Konzept gebunden, welches Sie besser akzeptieren sollten. Andernfalls werden Sie dazu gezwungen sein, in einer Datei Namens routes.json
4. Ein eigener Server
Einen eigenen Server schreiben, nur um im Frontend gemockte Daten zur Verfügung zu haben? Klingt zunächst mal nach einem perfekten Beispiel für Over-Engineering. Die Frage ist: Wollen Sie – als Frontend-Entwickler – ein »zweites« Backend pflegen? Der Hauptgrund, warum Sie Ihre Daten mocken möchten, ist es ja schließlich, vom Backend unabhängig zu sein.
Die Antwort ist ja. Zumindest unter der Voraussetzung, einen vernünftigen Kompromiss zwischen Flexibilität und Komplexität erzielen zu können.
Das HTTP-Fake-Backend bietet diesen Kompromiss. Es kommt als Node.js-Server, der Dummy-Daten über HTTP bereitstellt, und ist fast so einfach einzurichten wie der oben beschriebene JSON-Server. Es versetzt Sie in die Lage, schnell und einfach eine API bauen – schlichtweg durch die Bereitstellung von JSON-Dateien oder JavaScript-Objekten über einfach zu konfigurierende Routen.
Jeder Endpunkt benötigt eine Konfigurationsdatei in den Routen, damit HTTP-Methoden und Response definiert werden können.
Einfaches Beispiel:
Sagen wir mal, Sie benötigen einen Endpunkt wie http://localhost:8081/api/simpleExample, der folgendes JSON zurückgeben soll:
„status“: „ok“
Alles was Sie machen müssen, ist die folgende Konfiguration in einer JavaScript-Datei bereitzustellen:
module.exports = SetupEndpoint({ name: 'simpleExample', urls: [{ requests: [{ response: {status: 'ok'} }] }] });
Weiterführendes Beispiel:
Das Fake-Backend kann wesentlich mehr als ein durch JavaScript-Objekte definiertes JSON via HTTP GET zurückzugeben. Lassen Sie uns eine komplexere Beispielskonfiguration betrachten, die wie folgt aussieht:
module.exports = SetupEndpoint({ name: 'anotherExample', urls: [{ params: '/read', requests: [{ method: 'GET', response: '/json-templates/anotherExample.json' }] }, { params: '/update/{id}', requests: [{ method: ['PUT', 'PATCH'], response: { success: true } }, { method: 'DELETE', response: { deleted: true } }] }] });
Sie können also für einen Endpunkt mehrere URLs mit unterschiedlichen HTTP-Methoden festlegen und neben JavaScript-Objekten auch die Inhalte von JSON-Dateien aus dem File-System als Response definieren.
Auch HTTP-Fehler und Statuscodes lassen sich sowohl für einen gesamten Endpunkt als auch auf Request-Level wie folgt simulieren:
module.exports = SetupEndpoint({ name: 'statusCodes', urls: [ { params: '/boomError', requests: [{ // Returns a 402 status code + error message provided by boom: // { // "error" : "Payment Required", // "statusCode" : 402 // } statusCode: 402 }] }, { params: '/customError', requests: [{ // Returns a HTTP status code 406 and a self defined response: response: { error: true }, statusCode: 406 }] }, { params: '/regularResponse', requests: [{ // Returns a 401 error provided by boom // as defined on endpoint level response: '/json-templates/anotherExample.json' }] } ], statusCode: 401 });
Beschreibung des Konfigurationsobjekts
name (String)
- wird genutzt, um den Endpunkt zu setzen.
urls (Array)
- mindestens ein URL-Objekt hinzufügen.
urls.params (String)
- optional
- URL Path Parameters mit festen oder variablen Pfad-Bestandteilen
- Siehe hapi-Dokumentation. Zum Beispiel bezüglich optionalen Path Parameters
urls.requests (Array)
- mindestens ein Request-Objekt hinzufügen
- mehrere Request-Objekte sind nur nötig, wenn Sie bei verschiedenen HTTP-Methoden unter einer identischen URL verschiedenen Responses zurück geliefert werden sollen
urls.requests.method (String|Array)
- optional. Benutzt
GET
, sofern nicht definiert - wird benutzt, um die HTTP-Methode(n) zu definieren, auf die der Endpoint reagieren soll
urls.requests.response (String|Array)
- kann ein String sein, der auf ein JSON-Template zeigt:
response: '/json-templates/articles.json'
- oder einfach ein JavaScript Object:
response: { success: true }
urls.requests.statusCode (Number)
- optional
- Statuscode
- Sorgt für folgende Response:
- Einen Statuscode mit einer selbst definierten Antwort, wenn Sie zusätzlich eine Response-Property definiert haben.
Einen Statuscode mit einem vordefinierten Error-Objekt, bereitgestellt durch boom, wenn Sie für diesen Request keine Response-Property definiert haben.
statusCode (Number)
- optional
- Jede Route dieses Endpoints wird einen HTTP-Error mit dem hier definierten Status-Code zurückgeben
Falls diese Konfigurationsarbeit abschreckt, kann man sich Konfiguration einfach und interaktiv mit Hilfe dieses Yeoman Generator erstellen lassen. Er führt mit wenigen Fragen durch die Konfiguration:
Es geht noch flexibler
Das ergibt natürlich nur Sinn, wenn Sie es wirklich benötigen: Sie können auch an den vorgelieferten Konfigurationsmöglichkeiten vorbei eigene Endpunkte mit benutzerdefinierten Konfigurationen bauen. Denn der zugrundeliegende Server ist ein gewöhnlicher Node.js-HTTP-Server auf Basis von hapi. Damit lässt sich zum Beispiel leicht ein Endpunkt realisieren, der HTML statt JSON liefert.
Pro:
- Die Dummy-Daten sind in der Versionskontrolle,
- höchste Flexibilität,
- einfache Einrichtung,
- Endpoints,
- URLs einschließlich dynamischer Teile,
- erlaubte HTTP-Methoden,
- Errors (mit Unterstützung für alle HTTP-Statuscodes),
- Yeoman Generator,
- mit Subgenerator, um beim Aufbau der API zu unterstützen.
Der Server startet sich selbständig und blitzschnell neu, wenn JSON oder Konfigurationsdateien geändert werden. Genau wie der JSON-Server.
Contra:
- Etwas komplexeres Setup im Vergleich zum JSON-Server,
- weniger intelligent als der JSON-Server,
- kein Speichern von Payload-Daten in den Datenquellen für POST, PUT und PATCH-Anfragen,
- keine automatische und reale Beziehung zwischen Daten von verschiedenen Endpunkten.
Beispiel: Man muss eigene und unabhängige Daten für eine Route definieren, die eine Liste /api/products und eine Route, welche ein einzelnes Element /api/products/{id} enthält, zurück gibt.
Workflow-Integration
Der Server lässt sich während der Entwicklung mit npm run start:dev starten. Bei Änderungen an Endpunkten startet er automatisch neu. Dagegen ist ein Starten des Servers via npm start etwa in einer Continous-Integration-Umgebung sinnvoll. Somit sollte sich der Server in Ihren vorhandenen Workflow gut integrieren lassen. Unabhängig davon, ob Sie npm-run-Skripte oder einen Taskrunner benutzen, um Ihr Frontend zu bauen.
Los geht’s!
Besuchen Sie einfach die Projektseite auf Github und Ihr Fake-Backend läuft in wenigen Minuten. Komfortabler geht es, wenn Sie den Yeoman-Generator verwenden.
Fazit
Wir Entwickler sind nicht faul, wenn wir versuchen, stupide und sich wiederholende Arbeit zu vermeiden. Vielmehr sind wir auf Effizienz und die Sinnhaftigkeit unseres Tuns aus. Deshalb können wir bei der Erzeugung der Dummy-Daten für unser Fake-Backend Hilfe gebrauchen. Dabei können die folgenden Tools nützlich sein:
JSON-Generatoren:
Die folgenden Tools helfen Ihnen, JSON für das Fake-Backend zu generieren:
- https://github.com/danibram/mocker-data-generator/
- http://www.json-generator.com/
Dummydaten-Generatoren:
Ich empfehle, eines der folgenden ausgezeichneten Node-Module zu verwenden, wenn Sie es vorziehen, Ihre Fake-Daten programmatisch zu erstellen:
- https://github.com/Marak/faker.js
- https://github.com/boo1ean/casual
- https://github.com/chancejs/chancejs/