api-validation
@rtorcato/api-validation parses incoming data with a zod schema and throws a BadRequestError on failure — which the error-handler middleware converts to a 400 JSON response automatically.
Install
pnpm add @rtorcato/api-validation
Usage
import { validate } from '@rtorcato/api-validation'
import { z } from 'zod'
const createItemSchema = z.object({
name: z.string().min(1),
quantity: z.coerce.number().int().positive(),
})
// Express
app.post('/items', (req, res) => {
const body = validate(createItemSchema, req.body)
// body is typed: { name: string; quantity: number }
// ...
})
// Hono
app.post('/items', async (c) => {
const body = validate(createItemSchema, await c.req.json())
// ...
})
On failure, validate throws a BadRequestError with a formatted message:
{
"error": "BadRequestError",
"code": "validation_error",
"message": "Error #1: Required at name; Error #2: Expected number, received nan at quantity"
}
What it does
validate(schema, data) is a thin wrapper:
- Calls
schema.safeParse(data) - On success → returns the typed result
- On failure → formats the
ZodErrorinto a readable string and throwsnew BadRequestError(message, 'validation_error')
The BadRequestError is caught by errorHandler() from api-errors-express or api-errors-hono and converted to a 400 response.
Format errors without throwing
If you need the formatted error string without throwing:
import { formatZodError } from '@rtorcato/api-validation'
const result = schema.safeParse(data)
if (!result.success) {
const message = formatZodError(result.error)
// handle however you like
}
Validate route params / query strings
validate works on any data, not just request bodies:
const paramsSchema = z.object({ id: z.string().uuid() })
app.get('/items/:id', (req, res) => {
const { id } = validate(paramsSchema, req.params)
// ...
})