zod

9. Juni 2025

Zod ist eine TypeScript-first Schema Validation Library, die ich in einigen Projekten verwendet habe. Mit der Veröffentlichung von Version 4 wurden einige interessante Verbesserungen ergänzt.

Die komplette Dokumentation zu Zod findest du unter zod.dev.

Was ist Zod eigentlich?

Bevor wir in die Details von v4 einsteigen, kurz zur Einordnung: Zod löst ein Problem, das jeder TypeScript-Entwickler kennt. TypeScript macht zwar zur Compile-Zeit großartiges Static Type Checking, aber zur Runtime ist davon nichts mehr übrig. Sprich: wenn User Input von irgendwo reinkommt (API, Forms, …), dann weißt du nicht, ob die Daten wirklich das Format haben, das du erwartest.

// TypeScript denkt, das ist alles ok...
interface User {
  name: string;
  age: number;
}

// ...aber zur Runtime kann das hier reinkommen:
const userInput = { name: 42, age: "definitely not a number" };

Zod schließt diese Lücke, indem es Schema Validation zur Runtime ermöglicht und dabei automatisch die TypeScript-Typen ableitet:

import { z } from "zod/v4";

const User = z.object({
  name: z.string(),
  age: z.number()
});

type User = z.infer<typeof User>; // automatisch abgeleiteter Type

// Sichere Validierung zur Runtime
const result = User.safeParse(userInput);
if (result.success) {
  console.log(result.data.name); // Type-safe!
} else {
  console.log(result.error); // ZodError mit Details
}

Native JSON Schema Support

Ein Feature, auf das viele gewartet haben: Zod kann jetzt nativ zu JSON Schema konvertieren, ohne externe Libraries wie zod-to-json-schema:

const User = z.object({
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(18)
});

// Neu in v4: direkte Konvertierung
const jsonSchema = User.toJSON();

Das ist besonders praktisch für API Documentation, Form Builder oder wenn man mit Backend-Systemen arbeitet, die JSON Schema erwarten. Auch viele AI-Modelle unterstützen mittlerweile ein von Zod erstelltes Schema für bspw. Responses.

Verbesserte Error Messages

Die Fehlermeldungen wurden überarbeitet und sind jetzt deutlich informativer:

const schema = z.object({
  user: z.object({
    profile: z.object({
      email: z.string().email()
    })
  })
});

const result = schema.safeParse({
  user: {
    profile: {
      email: "not-an-email"
    }
  }
});

if (!result.success) {
  console.log(result.error.format());
}

Neue Schema-Features

z.interface() für exakte Object Shapes

Eine der interessantesten Neuerungen ist z.interface(), das eine exaktere Kontrolle über Object Schemas ermöglicht:

// Mit z.object() werden unbekannte Properties ignoriert
const UserObject = z.object({
  name: z.string(),
  age: z.number()
});

// Mit z.interface() werden sie strikt validiert
const UserInterface = z.interface({
  name: z.string(),
  age: z.number()
});

UserInterface.parse({ name: "Stefan", age: 30, extra: "not allowed" });
// Fehler: unbekannte Eigenschaft 'extra'

Bessere Union Types mit Auto-Discriminator

Zod v4 erkennt automatisch Discriminator in Union Types:

const Event = z.union([
  z.object({
    type: z.literal("click"),
    element: z.string()
  }),
  z.object({
    type: z.literal("scroll"),
    position: z.number()
  })
]);

// Zod erkennt automatisch 'type' als Discriminator
// und kann dadurch effizienter validieren

File Validation

Neu ist auch die native Unterstützung für File Validation im Browser:

const FileSchema = z.file({
  maxSize: 1024 * 1024 * 5, // 5MB
  types: ["image/jpeg", "image/png"]
});

// Für File Inputs in Forms
const result = FileSchema.safeParse(fileInput.files[0]);

Praxisbeispiel: API Validation

Hier ein kleines Beispiel, wie sich Zod v4 in der Praxis anfühlt:

import { z } from "zod/v4";

// Schema Definition
const CreateUserRequest = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
  age: z.number().min(18).max(120),
  preferences: z.object({
    newsletter: z.boolean().default(false),
    theme: z.enum(["light", "dark"]).default("light")
  }).optional()
});

// Type Inference
type CreateUserRequest = z.infer<typeof CreateUserRequest>;

// API Handler
async function createUser(req: Request) {
  const parseResult = CreateUserRequest.safeParse(await req.json());
  
  if (!parseResult.success) {
    return Response.json({
      error: "Validation failed",
      details: parseResult.error.format()
    }, { status: 400 });
  }
  
  const userData = parseResult.data; // Type-safe!
  // ... User erstellen
}

// JSON Schema für API Docs
const apiDocs = CreateUserRequest.toJSON();