Zum Hauptinhalt springen

JSON zu Zod

JSON zu Zod erzeugt ein lauffähiges Zod-Schema aus einem Beispieldokument direkt in Ihrem Browser. Anders als ein TypeScript-Interface ist die Ausgabe Code, der wirklich läuft: Er parst einen Wert zur Laufzeit und verwirft, was die Stichprobe nicht enthielt. Fügen Sie mehrere Datensätze ein, damit optionale und nullable Felder erschlossen statt geraten werden.

Nur lokal
Zod-Version
Beispiel-JSON
0 B1 ZeileZ. 1, Sp. 1
Syntaxfehler · Fehler in Zeile 1, Spalte 1

Das Dokument endet zu früh – es scheint abgeschnitten.


^
Generierter Codeschreibgeschützt
0 B1 Zeile
Fügen Sie nach Möglichkeit mehrere Datensätze ein. Nur so werden optionale Felder erkannt, die nicht in jeder Zeile vorkommen.

Wie funktioniert JSON zu Zod?

Ein TypeScript-Interface und ein Zod-Schema sehen ähnlich aus und machen völlig unterschiedliche Jobs. Das Interface wird gelöscht, wenn der Code kompiliert: Es sagt Ihrem Editor, wie die Form aussehen soll, und dem laufenden Programm nichts. Ein Zod-Schema ist ein Wert, der bis in die Laufzeit überlebt, sodass es eine echte Antwort parsen und ablehnen kann. Das ist der Unterschied zwischen darauf hoffen, dass die API ihr Versprechen hielt, und prüfen, ob sie es tat.

Dieselbe Inferenz, eine andere Ausgabe

Dieses Tool teilt seine Typinferenz mit den fünf Sprach-Generatoren daneben – ein einziger Durchlauf durch die Stichprobe entscheidet, welche Felder optional sind, welche nullable, welche Objektformen identisch sind und was der Elementtyp eines gemischten Arrays ist. Nur die Ausgabe unterscheidet sich. Das ist wichtig, weil diese Entscheidungen der schwierige Teil sind und eine zweite Implementierung irgendwann von der ersten abweichen würde.

Optional und nullable sind zwei verschiedene Dinge

{"a": null} und {} sind verschiedene Dokumente. Zod schreibt sie auch unterschiedlich – .nullable() für das erste, .optional() für das zweite – und keins impliziert das andere. Ein Schlüssel, der mal fehlt und mal null ist, braucht beides, in dieser Reihenfolge: z.string().optional().nullable(). Das zu verwechseln ist der übliche Grund, warum ein generierter Validator Ihre Fixtures besteht und in der zweiten Produktionswoche scheitert.

Formatprüfungen sind eine Behauptung über jeden Wert

Die Inferenz merkt, wenn ein String wie eine UUID, eine E-Mail oder ein Datum mit Uhrzeit aussieht, und kann z.uuid(), z.email() oder z.iso.datetime() statt eines bloßen z.string() ausgeben. Das ist wirklich nützlich und ein wenig gefährlich: Es ist eine Behauptung über jeden Wert, den das Feld je tragen wird, erschlossen aus einer Stichprobe. Wenn eine Spalte in Ihrem Test-Fixture eine UUID und in Produktion ein Slug ist, wird der Validator echte Daten ablehnen. Deshalb ist es ein Schalter, und die Referenztabelle unten sagt, welcher Validator zu jedem erkannten Format gehört.

Zod 3 und Zod 4 schreiben Formate unterschiedlich

Zod 4 hat die String-Format-Validatoren auf die oberste Ebene verschoben: z.uuid(), z.email(), z.url() und z.iso.datetime(), als Ersatz für die als deprecated markierte z.string().uuid()-Familie. Beide laufen weiterhin, das Ziel ist also eine Wahl statt einer Vermutung, und der Standard ist Zod 4, denn ein heute erzeugtes Schema geht in eine Codebasis, die heute beginnt.

JSON-Beispiel

{
  "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
  "email": "[email protected]",
  "score": 148,
  "deleted_at": null,
  "tags": ["priority", "gift"]
}

Zod-Schema

export const RootSchema = z.object({
  id: z.uuid(),
  email: z.email(),
  score: z.number().int(),
  deleted_at: z.unknown(),
  tags: z.array(z.string()),
});
export type Root = z.infer<typeof RootSchema>;

JSON-Beispiel → Zod-Schema

JSON-Beispiel

{
  "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
  "email": "[email protected]",
  "score": 148,
  "deleted_at": null,
  "tags": ["priority", "gift"]
}

Zod-Schema

import { z } from "zod";

export const RootSchema = z.object({
  id: z.uuid(),
  email: z.email(),
  score: z.number().int(),
  deleted_at: z.unknown(),
  tags: z.array(z.string()),
});
export type Root = z.infer<typeof RootSchema>;

Welche Optionen und Sonderfälle unterstützt JSON zu Zod?

Technische Referenz und Optionen
ParameterTypStandardVerhalten & Sonderfälle
Zod-VersionZod 3 | Zod 4Zod 4Zod 4 nutzt die top-level-Funktionen z.uuid(), z.email(), z.url() und z.iso.datetime(); Zod 3 nutzt die z.string().uuid()-Familie, die Zod 4 zwar noch akzeptiert, aber als deprecated markiert. Alles andere in der Ausgabe ist identisch.
strict-ObjekteBooleanausFügt .strict() hinzu, sodass ein Schlüssel, den das Schema nicht kennt, ein Fehler ist, statt stillschweigend verworfen zu werden. Sinnvoll für einen Request-Body, den Sie validieren; meistens falsch bei einer Antwort von Dritten, die Felder hinzufügt, ohne es Ihnen zu sagen.
z.infer-TypenBooleaneinGibt export type X = z.infer<typeof XSchema> neben jedem Schema aus, damit der TypeScript-Typ und der Laufzeit-Validator nicht auseinanderdriften können. Das ist die eine Stelle, an der Zod einem handgeschriebenen Interface klar überlegen ist.
FormatprüfungenBooleaneinWendet z.uuid(), z.email(), z.url(), z.iso.datetime() und z.iso.date() an, wo die Stichprobe danach aussah. Eine Inferenz aus einer einzigen Stichprobe ist eine starke Behauptung – schalten Sie sie aus, wenn eine Spalte in Produktion nicht homogen ist.
Optionalitäterschlossenüber Datensätze hinwegEin Schlüssel, der in mindestens einem Datensatz derselben Form fehlt, erhält .optional(). Fügen Sie mehrere Datensätze ein; ein einzelner Datensatz kann das nicht ausdrücken, und ein Schema aus einem Datensatz wird den zweiten ablehnen.
Nullableerschlossen.nullable()Ein Wert, der in manchen Datensätzen null und in anderen ein String ist, wird zu z.string().nullable(). Ein Schlüssel, der mal fehlt und mal null ist, erhält .optional().nullable() – das sind zwei verschiedene Anforderungen und keine Redundanz.
Ganzzahlerschlossenz.number().int()Eine JSON-Zahl ohne Nachkommastellen wird zu einem Ganzzahl-Schema, sodass ein Float, der ankommt, wo die Stichprobe eine ganze Zahl hatte, abgelehnt statt stillschweigend akzeptiert wird. Ein Feld, das in einem Datensatz 1 und in einem anderen 1.5 ist, wird zu einem schlichten z.number().
Root-ArrayDokumentformz.array(RootSchema)Ein Array aus Datensätzen auf oberster Ebene gibt das Element-Schema plus einen RootSchema-Wrapper aus, sodass das Schema des Dokuments selbst eine Zeile ist und nichts, was Sie selbst zusammenbauen müssen.
EingabegrößeBytes2 MBDie Inferenz liest jeden Wert. Ein paar hundert repräsentative Datensätze beschreiben eine Form genauso gut wie eine Million, daher kostet die Obergrenze in der Praxis nichts.

Häufig gestellte Fragen

Warum ein Zod-Schema statt eines TypeScript-Interfaces?

Weil ein TypeScript-Typ zur Kompilierzeit gelöscht wird. Er sagt Ihrem Editor, wie die Form aussehen soll, und dem laufenden Programm nichts, sodass eine Antwort, die ihn verletzt, einfach durchrutscht. Ein Zod-Schema ist ein echter Wert, der zur Laufzeit existiert: safeParse liefert entweder die typisierten Daten oder die exakten Pfade, die fehlgeschlagen sind. Ist die API Ihre eigene und gut getestet, reicht ein Interface. Ist sie fremd oder stammt die Eingabe von Nutzern, ist ein Validator das Einzige, das wirklich prüft.

Warum hat mein Feld .optional(), obwohl die API es immer sendet?

Weil mindestens ein Datensatz in der Stichprobe es nicht hatte. Der Generator kann nur die Daten beschreiben, die er bekam. Fügen Sie eine Stichprobe ein, in der das Feld immer vorkommt, um ein Pflicht-Schema zu erhalten, oder löschen Sie das .optional() von Hand – richtig, wenn Sie den Vertrag besser kennen als die Stichprobe. Beachten Sie, dass das das Gegenteil des Fehlers von .nullable() ist: Ein fehlender Schlüssel und ein null-Wert sind verschieden, und das Schema unterscheidet sie.

Ist .nullable() dasselbe wie .optional()?

Nein, und sie zu verwechseln ist der häufigste Bug in generierten Schemas. {a: null} hat den Schlüssel mit einem null-Wert; {} hat den Schlüssel gar nicht. .nullable() akzeptiert das erste, .optional() das zweite, und ein Feld, das beides sein kann, braucht beides – z.string().optional().nullable(). Die Reihenfolge ist eine Frage der Lesbarkeit, nicht des Verhaltens, aber die Dokumentation von Zod schreibt optional zuerst.

Warum wurde ein einfacher String zu z.uuid() oder z.email()?

Weil jeder Wert, den die Inferenz an dieser Stelle sah, dem Muster entsprach, behandelte sie das Format als Teil des Typs. Meistens richtig und gelegentlich falsch: Eine Spalte, die in Ihrem Fixture UUIDs und in Produktion Slugs enthält, wird jetzt bei echten Daten die Validierung scheitern lassen. Schalten Sie die Formatprüfungen aus, um überall ein bloßes z.string() zu erhalten, oder editieren Sie die eine Zeile. Die Referenztabelle listet exakt, welches Format zu welchem Validator gehört.

Auf welche Zod-Version sollte ich zielen?

Zod 4, außer wenn Sie an Zod 3 gebunden sind. Zod 4 hat die Format-Validatoren auf die oberste Ebene verschoben – z.uuid() statt z.string().uuid() – und die alten Schreibweisen als deprecated markiert, sodass ein für Zod 4 erzeugtes Schema in einer neuen Codebasis keine Deprecation-Warnungen hat. Die alten Aufrufe funktionieren in Zod 4 weiterhin, weshalb ein Zod-3-Schema in einem Zod-4-Projekt nicht kaputt ist, nur veraltet.

Wird mein JSON irgendwo hochgeladen?

Nein. Inferenz und Darstellung laufen beide in dem Tab, den Sie ohnehin offen haben – öffnen Sie DevTools, beobachten Sie das Netzwerk-Panel und fügen Sie ein Dokument ein. Das wiegt hier schwerer als bei den meisten Tools, denn die Dokumente, die Leute in Validatoren verwandeln, sind Produktions-API-Antworten, häufig mit echten Kundendatensätzen darin.