Query Parameters
How query parameters are parsed and coerced from strings to their expected types.
For GET and DELETE endpoints, input comes from URL query parameters. Since query params are always strings, the handler automatically coerces them to match your Zod schema types.
Supported coercions
| Zod type | Query string | Coerced value |
|---|---|---|
z.number() | ?count=42 | 42 |
z.boolean() | ?active=true | true |
z.boolean() | ?active=0 | false |
z.bigint() | ?id=9007199254740993 | 9007199254740993n |
z.date() | ?after=2024-01-01T00:00:00Z | Date object |
z.array(z.number()) | ?ids=1&ids=2 | [1, 2] |
z.string() | ?name=alice | 'alice' (no coercion) |
Array handling
Single query values are automatically wrapped into arrays when the schema expects an array:
// Schema
z.object({ tags: z.array(z.string()) })
// ?tags=alpha → { tags: ['alpha'] }
// ?tags=a&tags=b → { tags: ['a', 'b'] }Array elements are individually coerced:
// Schema
z.object({ ids: z.array(z.number()) })
// ?ids=1&ids=2&ids=3 → { ids: [1, 2, 3] }Optional and default values
Optional and default fields work as expected:
z.object({
page: z.number().default(1),
size: z.number().default(20),
active: z.boolean().optional(),
})
// No query params → validated as {} → defaults applied by Zod
// ?page=5 → { page: 5 } → size defaults to 20Schema safety
Coercion never mutates your Zod schemas. The handler creates new objects with coerced values and passes them through Zod validation. Invalid values (e.g. ?count=abc for a number field) are left as strings and caught by Zod validation, returning a BAD_REQUEST error.