Zurück zur Startseite

API-Validierung zur Laufzeit mit Zod und TypeScript

Der Artikel erklärt TypeScript-Beschränkungen zur Laufzeit für API-Daten und schlägt Schema-Validierung mit Zod vor. Code-Beispiele und Bibliotheksvergleiche für Middle-/Senior-Entwickler.

Zod für Laufzeit-Validierung von TypeScript API
Advertisement 728x90

Runtime-Validierung von API-Antworten: Warum TypeScript nicht reicht

TypeScript prüft Typen nur zur Kompilierzeit. Daten aus APIs kommen zur Laufzeit an – ohne Garantie, dass sie zu euren Interfaces passen. Ohne zusätzliche Validierung führen Abweichungen zu Fehlern, die Nutzer sehen: undefinierte Felder, falsche Typen, fehlende Objekte.

Probleme ohne Laufzeitprüfungen

TypeScript-Interfaces vermitteln ein falsches Sicherheitsgefühl. Hier typischer Code ohne Validierung:

interface User {
  id: number;
  name: string;
  email: string;
}

const getUser = async (): Promise<User> => {
  const res = await fetch('/api/user');
  return res.json(); // Keine Prüfung der echten Daten
};

TypeScript vertraut auf die Typannotation und ignoriert die echte Server-Antwort. Wichtige Risiken:

Google AdInline article slot
  • Backend-Änderungen: full_namefullName führt zu undefined.
  • Typkonflikte: String "123" statt Number 123 verursacht Verkettung statt Addition.
  • Bedingte Felder: address fehlt in Randfällen, Absturz bei user.address.city.
  • Third-Party-APIs mit veralteter Dokumentation.

Schema-Validierungs-Bibliotheken

Die Lösung? Bibliotheken für Laufzeit-Schema-Validierung. Sie parsen echte Daten und werfen Fehler bei Abweichungen. Top-Optionen für TypeScript:

  • Zod: Leitet Typen aus Schemas ab, riesiges Ökosystem.
  • Yup: Passt super zu Formik.
  • Valibot: Bundle-freundlich, tree-shakable.
  • Joi: Node.js-Wurzeln, backend-orientiert.
  • ArkType: Performance-fokussiert.

Umsetzung mit Zod

Zod vereint Schema, Typen und Validierung in einem. User-Beispiel:

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

type User = z.infer<typeof UserSchema>;

const getUser = async (): Promise<User> => {
  const res = await fetch('/api/user');
  const data = await res.json();
  return UserSchema.parse(data); // Fehler bei Abweichung
};

parse() wirft einen ZodError mit Details bei Fehlern. Nutzt safeParse() für ein Objekt mit success und error.

Google AdInline article slot

Skalierung in echten Projekten

Bei großen Apps zentralisiert Schemas für alle Endpoints. Fetch so wrappen:

const apiFetch = async <T>(url: string, schema: z.ZodSchema<T>): Promise<T> => {
  const res = await fetch(url);
  const data = await res.json();
  return schema.parse(data);
};

const users = await apiFetch('/api/users', z.array(UserSchema));

Fehlerbehandlung: An Sentry loggen, nutzerfreundliche Meldungen zeigen. Bei Arrays jedes Element validieren.

  • z.array(UserSchema) für Listen.
  • z.union() für polymorphe Antworten.
  • z.lazy() für rekursive Strukturen.
  • z.transform() zum Normalisieren von Daten.

Wichtige Erkenntnisse

  • TypeScript garantiert keine Laufzeit-Typen für externe Daten: APIs, localStorage, URL-Parameter.
  • Schema-Validierung fängt Abweichungen früh ab, spart Debug-Zeit.
  • Zod ist Frontend-Standard: Typen aus Schemas, einfache Integration.
  • In Teams mit separatem Backend verhindert es stille Fehler.
  • Mit Fetch-Wrappers skalieren für Konsistenz.

Runtime-Validierung schließt die Lücke zwischen TypeScripts Kompilierzeit-Prüfungen und Ausführung. In der Produktion reduziert sie Bugs durch asynchrone API-Verträge massiv.

Google AdInline article slot

— Editorial Team

Advertisement 728x90

Weiterlesen