Contents

Programming Fundamentals › Type Systems

Runtime Validation

Checking that untrusted data matches a type at runtime, e.g. with Zod or Pydantic.

Also known as: Zod, Pydantic, schema validation, validating at runtime, runtime type checking, parsing untrusted data

Static types (TypeScript, Python type hints) exist only at compile time. They’re erased, and at runtime nothing checks that data actually has the shape the types claim. Data from outside your program (API responses, form input, files, environment variables, JSON from a database) is unknown until you validate it at runtime.

type User = { id: number; email: string };

const user = (await res.json()) as User;     // a promise to the compiler, not a check!
user.email.toLowerCase();                    // crashes if the API returned something else

The as cast tells TypeScript to trust you. If the server changes its response, the compiler can’t notice.

Validate with a schema library

Define the shape once, and parse unknown data against it:

import { z } from "zod";

const UserSchema = z.object({
  id: z.number().int(),
  email: z.string().email(),
  age: z.number().min(0).optional(),
});
type User = z.infer<typeof UserSchema>;      // the TypeScript type comes from the schema

const result = UserSchema.safeParse(await res.json());
if (!result.success) {
  console.error(result.error.issues);        // which fields failed, and why
} else {
  const user: User = result.data;            // now genuinely typed
}
from pydantic import BaseModel, EmailStr

class User(BaseModel):
    id: int
    email: EmailStr
    age: int | None = None

user = User.model_validate(payload)          # raises ValidationError with details

Zod (TypeScript) and Pydantic (Python) are widely used examples. Others exist for most languages.

Where to use it

  • At the boundaries of your system: incoming requests (input validation), responses from other services, config and environment variables, data read from files and queues.
  • Not everywhere inside. Once validated, the rest of your code can trust the types.

Benefits

  • One source of truth for the shape, the type and the validation rules.
  • Clear errors that say which field is wrong, instead of a crash three functions away.
  • Safer evolution: when an upstream API changes, you find out at the edge and in tests.
  • Transformation as well as checking: coercing strings to numbers or dates, setting defaults, stripping unknown fields.

Remember never trust the client. A JSON Schema is the language-neutral form of the same idea.