Teilen eines einzigen Zod-Schemas zwischen Ihrer React-Frontend- und Node-Backend-Anwendung
Erfahren Sie, wie ein einzelnes Zod-Schema React-Formulare, API-Antworten, Express-Anfragekörper sowie Umgebungsvariablen validieren kann und gleichzeitig passende TypeScript-Typen erzeugt.
Die Validierung gehört in jede Anwendung, doch Teams fügen sie oft nur stückweise hinzu – eine Bibliothek für die Frontend- und eine andere für das Backend, wobei dieselben Regeln an mehreren Stellen kopiert und eingefügt werden. Zod ist gerade deshalb bei JavaScript- und TypeScript-Entwicklern beliebt, weil es dieses Chaos vermeidet: Man schreibt ein einziges Schema, und dieses prüft die Daten sowie erzeugt den entsprechenden TypeScript-Typ, der sowohl im Browser als auch auf dem Server identisch verwendet werden kann.
1. Was ist Zod?
Zod ist eine Schema-Bibliothek, die von Anfang an mit TypeScript im Sinn entwickelt wurde. Man beschreibt einmal die Struktur der Daten, und Zod verwendet diese Beschreibung, um Werte zur Laufzeit zu prüfen und automatisch einen TypeScript-Typ abzuleiten – es gibt keine separate Schnittstelle zu schreiben, und es besteht kein Risiko, dass sie mit den Validierungsregeln aus dem Takt gerät.
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
type User = z.infer<typeof UserSchema>;
// { name: string; email: string; age?: number }
Ein solches einziges Schema deckt gleichzeitig drei Aufgaben ab: Es dokumentiert die Struktur Ihrer Daten, stellt diese zur Laufzeit sicher und liefert den statischen Typ, auf den Ihr Editor sowie der Compiler angewiesen sind.
2. Warum Zod den Alternativen überlegen ist
Der herausragende Vorteil ist die automatische Typableitung. Bibliotheken wie Yup oder Joi erfordern in der Regel, dass Sie neben einer manuell geschriebenen TypeScript-Schnittstelle auch ein Validierungsschema pflegen, in der Annahme, dass sich die beiden im Laufe von Codeänderungen nicht voneinander entfernen. Zod beseitigt dieses Risiko vollständig: Der Typ wird direkt aus dem Schema abgeleitet, sodass nichts synchronisiert werden muss.
Zod ist außerdem leichtgewichtig und frei von externen Abhängigkeiten, was ihn genauso gut für ein platzsparendes Frontend-Bundle wie für einen Node.js-Dienst eignet. Seine kettablen, komponierbaren API-Funktionen sorgen dafür, dass selbst komplexe Validierungen – wie verschachtelte Objekte, Unionen oder Felder, die voneinander abhängen – klar und lesbar bleiben, anstatt in einem Gewirr von ad-hoc-Hilfsfunktionen zu enden.
3. Verwendung von Zod in einer React-App
3.1 Formvalidierung mit React Hook Form
Zod wird über das Paket @hookform/resolvers direkt in React Hook Form integriert.
npm install zod react-hook-form @hookform/resolvers
// components/SignupForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const SignupSchema = z.object({
name: z.string().min(2, 'Name is too short'),
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type SignupData = z.infer<typeof SignupSchema>;
export function SignupForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<SignupData>({
resolver: zodResolver(SignupSchema),
});
const onSubmit = (data: SignupData) => {
console.log('Valid data:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Name" />
{errors.name && <p>{errors.name.message}</p>}
<input {...register('email')} placeholder="Email" />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register('password')} placeholder="Password" />
{errors.password && <p>{errors.password.message}</p>}
<button type="submit">Sign Up</button>
</form>
);
}
Es gibt keine manuelle Überwachung des Fehlerzustands und auch keine doppelten Typdeklarationen, die gepflegt werden müssten – ein einziges Schema kümmert sich um die Validierung, liefert die neben jedem Feld angezeigten Fehlermeldungen und definiert gleichzeitig den TypeScript-Typ des übermittelten data-Objekts.
3.2 Validierung von API-Antworten
Zod ist genauso nützlich auf der Eingangsseite Ihrer Anwendung – beispielsweise beim Überprüfen, ob die von einer API zurückgegebenen Daten tatsächlich mit dem übereinstimmen, was Sie erwartet haben, da man sich allein auf Kompilierzeittypen nicht verlassen kann, um dies zu garantieren.
import { z } from 'zod';
const PostSchema = z.object({
id: z.number(),
title: z.string(),
body: z.string(),
});
const PostsResponseSchema = z.array(PostSchema);
async function fetchPosts() {
const res = await fetch('/api/posts');
const json = await res.json();
const result = PostsResponseSchema.safeParse(json);
if (!result.success) {
console.error(result.error.flatten());
throw new Error('Invalid API response shape');
}
return result.data; // fully typed Post[]
}
Diese Vorgehensweise ermöglicht es, fehlerhafte oder unerwartete Antworten bereits vor dem Auftreten stummer Fehler in der Benutzeroberfläche abzufangen.
4. Verwendung von Zod in einem Node.js / Express Backend
4.1 Überprüfung von Anfragekörpern
npm install zod express
// schemas/user-schema.ts
import { z } from 'zod';
export const CreateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
// middleware/validate.ts
import { Request, Response, NextFunction } from 'express';
import { ZodSchema } from 'zod';
export function validate(schema: ZodSchema) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
req.body = result.data;
next();
};
}
// routes/users.ts
import { Router } from 'express';
import { validate } from '../middleware/validate';
import { CreateUserSchema } from '../schemas/user-schema';
const router = Router();
router.post('/users', validate(CreateUserSchema), (req, res) => {
// req.body is now guaranteed to match CreateUserInput
const { name, email, age } = req.body;
res.status(201).json({ name, email, age });
});
export default router;
Diese Konfiguration bietet jeder Route einen einheitlichen, deklarativen ValidierungsSchritt, wobei die Fehlerbehandlung zentralisiert statt als wiederholte inline if-Prüfungen in den Handlern erfolgt.
4.2 Überprüfung von Umgebungsvariablen
Eine unterschätzte, aber mächtige Verwendung von Zod besteht darin, process.env beim Start der Anwendung zu überprüfen, sodass eine fehlerhafte Konfiguration sofort zu einem Fehler führt anstatt später zu einem verwirrenden Bug.
// config/env.ts
import { z } from 'zod';
const EnvSchema = z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
NODE_ENV: z.enum(['development', 'production', 'test']),
});
export const env = EnvSchema.parse(process.env);
Falls eine erforderliche Variable fehlt oder das falsche Format hat, stürzt der Prozess sofort mit einer verständlichen Fehlermeldung ab – das ist viel einfacher zu diagnostizieren als ein rätselhafter Fehler, der in einem Datenbankaufruf verborgen ist.
5. Der eigentliche Vorteil: Ein Schema, das im gesamten Stack geteilt wird
Da Zod-Schemata lediglich TypeScript-Werte sind, hindert Sie nichts daran, sie in ein gemeinsames Paket – oder in einen gemeinsamen Ordner innerhalb eines Monorepos – zu platzieren und das identische Schema sowohl auf der Client- als auch auf der Serverseite zu wiederverwenden.
/packages
/shared
/schemas
user-schema.ts <-- used by both React app and Express API
/web (React/Next.js)
/api (Node/Express)
// packages/shared/schemas/user-schema.ts
import { z } from 'zod';
export const CreateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
Die React-App nutzt dieses Schema, um das Registrierungsformular vor der Übermittlung zu überprüfen. Die Express-API verlässt sich auf genau dasselbe Schema, um den eingehenden Datenstrom zu validieren. Wenn sich das Schema ändert – beispielsweise durch ein neues Pflichtfeld – passen sich beide Schichten gemeinsam an, und TypeScript zeigt sofort jeden Code auf, der sich noch nicht an die neue Struktur angepasst hat. Dadurch werden ganze Gruppen von Fehlern vermieden, bei denen sich die Validierung auf Client- und Serverseite im Laufe der Zeit unbemerkt voneinander entfernt.
6. Best Practices
- wählen Sie
safeParse, wenn ein Fehler ein normales, erwartetes Ergebnis ist (Formulareingaben, Antworten von Drittanbieter-APIs), und reservieren Sieparse– das Fehler auslöst – für Fälle, die tatsächlich niemals ungültig sein dürfen, wie beispielsweise Umgebungsvariablen bei der Startzeit.
.transform(), um im Rahmen der Validierung selbst Daten zu bereinigen – beispielsweise Leerzeichen zu entfernen oder Typen umzuwandeln – anstatt anschließend eine separate Normalisierungsphase durchzuführen.z.infer vor manuell geschriebenen Schnittstellen, wenn bereits ein Schema vorhanden ist, damit Ihre Typen und die Validierungslogik niemals aus dem Einklang geraten.error.flatten() oder error.format() zurück, damit der Frontend-Code jeden Fehler leicht auf das entsprechende Formularfeld zuordnen kann.7. Fazit
Zod ist mehr als nur eine typische Validierungsbibliothek – er verändert die Beziehung zwischen Validierung und Typisierung völlig. Indem er TypeScript-Typen direkt aus Laufzeit-Schemata generiert, beseitigt er problemlos das ganze Problem der auseinanderdriftenden Typdefinitionen und Validierungsregeln. Hinzu kommt sein geringer Ressourcenverbrauch, sein komponierbares Design sowie ein konsistentes Verhalten unabhängig davon, ob er im Browser oder in Node läuft – dadurch eignet sich Zod hervorragend für vollstack-basierte TypeScript-Projekte, die auf React und Node.js aufbauen.
Nächste Schritte:
- Betrachten Sie
zod-to-openapi, wenn Sie OpenAPI-Dokumentation direkt aus Ihren Schemata generieren müssen - Erkunden Sie
.refine()und.superRefine(), um benutzerdefinierte Validierungslogik zu erstellen, die mehrere Felder umfasst - Schauen Sie sich tRPC an, das auf Zod-Schemata aufbaut, um eine end-to-end-Typsicherheit in Ihrer API zu gewährleisten
Verwandte Artikel
- Zod vs express-validator: Zwei Ansätze für Express-Validierung — Vergleicht die schema-basierte Anfragenvalidierung mit Zod mit dem auf Ketten basierenden express-validator-Middleware, wobei Setup, Fehlerformatierung und häufige Fallstricke behandelt werden.
- TC39-Vorschläge im Jahr 2026: Decorators, Temporal und Signals erläutert — Ein praktischer Überblick über drei TC39-Vorschläge – native Decorators, die Temporal-API und Signals – sowie deren Bedeutung für Full-Stack-JavaScript- und TypeScript-Entwickler.