Search docs

Jump between documentation pages.

Validation

DaloyJS validates inputs through Standard Schema — a tiny interface that Zod, Valibot, ArkType, and TypeBox all implement. Pick whichever validator fits your project.

What gets validated

For each route you can declare schemas for:

  • request.params — path parameters (always strings; coerce in your schema if needed).
  • request.query — query string.
  • request.headers — request headers.
  • request.body — parsed JSON body. Only read when declared (no overhead otherwise).
  • responses[status].body — typed responses.

Example with Zod

ts
import { z } from "zod";

app.route({
  method: "POST",
  path: "/orders",
  operationId: "createOrder",
  request: {
    body: z.object({
      sku: z.string(),
      qty: z.number().int().positive(),
    }),
  },
  responses: {
    201: {
      description: "Created",
      body: z.object({ id: z.string().uuid(), sku: z.string(), qty: z.number() }),
    },
    422: { description: "Validation failed" },
  },
  handler: async ({ body }) => ({
    status: 201,
    body: { id: crypto.randomUUID(), sku: body.sku, qty: body.qty },
  }),
});

On invalid input, DaloyJS returns 422 Unprocessable Entity as RFC 9457 problem+json with the per-issue path and message array.

Body limits and content types

When a route declares request.body, DaloyJS will also enforce:

  • Content-Length / streamed size against app.bodyLimitBytes413.
  • Content-Type against app.allowedContentTypes (default application/json) → 415.
  • Prototype-pollution-safe JSON parsing (__proto__, constructor, prototype stripped).

Other validators

ts
// Valibot
import * as v from "valibot";
const Body = v.object({ sku: v.string(), qty: v.pipe(v.number(), v.integer(), v.minValue(1)) });

// ArkType
import { type } from "arktype";
const Body = type({ sku: "string", qty: "1<=number.integer" });

// TypeBox
import { Type } from "@sinclair/typebox";
const Body = Type.Object({ sku: Type.String(), qty: Type.Integer({ minimum: 1 }) });

All four expose a ~standard property that DaloyJS picks up automatically.

Type inference

Whatever validator you use, the handler context is fully typed: body, params,query, and headers are inferred from your schemas. The return value is also typed — TypeScript yells if you return a status not declared in responses.