Die weitere Verwendung der Daten ist dann eine mögliche Fehlerquelle, falls unerwartete Daten empfangen und ungeprüft verarbeitet werden. Beispielsweise könnte ein String fälschlicher Weise leer sein, oder ein Datumsfeld ist nicht lesbar, da es nicht einem vereinbarten Format entspricht. Den Fehlerquellen vorzubeugen ist zwar zunächst die Aufgabe der Entwickler:innen, hängt aber auch von den technischen Möglichkeiten ab. Wie eine einfache und trotzdem sichere Validierung gelingen kann, soll im Folgenden gezeigt werden.
Initial werden Daten oft als JSON-String entgegengenommen. In Typescript steht zunächst die Funktion JSON.parse
aus der Javascript Standardbibliothek zur Verfügung, um aus dem String ein Objekt zu erhalten. Das Resultat ist dann aber komplett untypisiert. Die einzig stattfindende Validierung ist, ob ein Objekt aus dem String gebaut werden kann. Ohne die Nutzung von weiteren Bibliotheken müsste jede weitere Valierung per Hand implementiert werden.
Angenommen, man habe einen einfachen Typ Movie:
Wenn wir nun ein Movie Objekt als JSON-String bekommen, können wir es mit JSON.parse
parsen:
Wie in dem Beispiel könnte man für die movie
Konstante eine Typ-Annotation nutzen, da wir ein Objekt vom Typ Movie
erwarten. Da JSON.parse
den Typ any
zurückgibt ist das immer erlaubt und löst keinen Fehler aus. Gleichzeitig ist es keine gute Idee. Sobald any
genutzt wird findet keine Typvalidierung statt.
In dem folgenden Beispiel würde man dadurch Laufzeitfehler auslösen, sobald der JSON-String kein gültiges Movie Objekt darstellt und man versuchen würde, das Ergebnis wie ein Movie Objekt zu benutzen.
Um dieser Kategorie von Problemen vorzubeugen, gibt es verschiedene Bibliotheken für die Validierung von Objekten. Dabei gibt es zwei unterschiedliche Arten von Bibliotheken: Entweder validieren sie bereits das JSON-Schema oder das daraus abgeleitete Javascript-Objekt.
JSON-Schemas sind durch die Internet Engineering Task Force standardisiert und haben den Vorteil, dass sie portabel sind. Man könnte dadurch z.B. das gleiche Schema für Backend und Fontend nutzen, selbst wenn dabei eine unterschiedliche Programmiersprache genutzt wird.
Bibliotheken, die nicht direkt mit dem JSON-Schema Standard arbeiten, sondern darauf spezialisiert sind, konkrete Javascript-Objekte zu validieren, bieten dafür eine übersichtlichere API und oftmals mehr Funktionen an.
Eine Bibliothek, die uns dabei besonders positiv augefallen ist, ist superstruct . Mit superstruct ist es sehr einfach, ein Schema zu erstellen und JavaScript-Objekte darauf zu testen. Dadurch könnte unser Beispiel so aussehen:
Die Funktion is prüft, ob das Object dem Schema entspricht, und gibt dann true oder false zurück. Alternativ könnte man mit assert auch eine Exception werfen.
Ein Vorteil von superstruct ist hier, dass die Fehlermeldung sehr gut lesbar ist. In diesem Beispiel wäre sie: StructError: At path: playerName -- Expected a string, but received: undefined
.
Neben einer Prüfung auf Vorhandensein und dem richtigen Typ von Feldern unterstützt superstruct auch das Prüfen der Werte von Feldern. In unserem Beispiel bietet es sich an, den title
als nicht leer und das yearOfRelease
als ganzzahlige Zahl zwischen 1900 und 2100 zu beschränken. Zusätzlich sollte das yearOfRelease
Feld eine ganze Zahl sein. Um Werte einzuschränken, werden dem Schema refinements hinzugefügt.
Ein weiteres Feature sind coercions. Dabei handelt es sich um Transformationen, die auf ein Feld vor dem Parsen und Validieren mit create
oder validate
angewendet werden. Ein praxisnahes Beispiel sind Datumsfelder. In APIs werden solche Felder oft als formatierte Strings versendet. Mit coercions kann man direkt prüfen, ob ein String ein valides Datum darstellt und dann aus dem String ein Datumsobjekt bauen:
Neben den oben genannten gibt es noch einige weitere Featues von superstruct. Viele werden auch durch alternative Bibliotheken wie yup, ow, zod, ajv unterstützt.
Wir fanden für unseren Anwendungsfall superstruct am geeignetesten, da es sich neben den vorgestellten Features in der Praxis als äußerst performant (Validierungen pro Minute) bewiesen hat.In komplexen Softwaresystemen werden regelmäßig Daten zwischen Komponenten ausgetauscht. Oft passiert das in REST-Abfragen, Nachrichten-Queues, Publish/Subscribe-Services, oder ganz klassisch als JSON- oder CSV-Datei.