Skip to content

Query and Path Parameters

Validate URL query parameters using the 'query' source:

import { YelixHono } from '@yelix/hono';
import { zValidatorYelix, type ZInfer } from '@yelix/zod-validator';
import { z } from 'zod';
const app = new YelixHono();
const querySchema = z.object({
page: z.string().transform(Number).pipe(z.number().int().positive()),
limit: z.string().transform(Number).pipe(z.number().int().positive().max(100)),
search: z.string().optional(),
});
app.get(
'/users',
zValidatorYelix('query', querySchema),
(c) => {
// ZInfer provides full type safety
const { page, limit, search }: ZInfer<typeof querySchema> = c.req.valid('query' as never);
return c.json({ page, limit, search });
}
);

Example request: GET /users?page=1&limit=10&search=john

Validate URL path parameters using the 'param' source:

import { zValidatorYelix, type ZInfer } from '@yelix/zod-validator';
const paramSchema = z.object({
id: z.string().uuid(),
});
app.get(
'/users/:id',
zValidatorYelix('param', paramSchema),
(c) => {
// ZInfer provides full type safety
const { id }: ZInfer<typeof paramSchema> = c.req.valid('param' as never);
return c.json({ userId: id });
}
);

Example request: GET /users/123e4567-e89b-12d3-a456-426614174000

Query parameters are always strings, so you may need to transform them:

// Transform string to number
z.string().transform(Number).pipe(z.number().int().positive())
// Transform string to boolean
z.string().transform((val) => val === 'true')
// Transform string to date
z.string().datetime()
// Multiple transformations
z.string()
.transform(Number)
.pipe(z.number().int().positive().max(100))

You can validate both in the same route:

app.get(
'/users/:id/posts',
zValidatorYelix('param', z.object({
id: z.string().uuid(),
})),
zValidatorYelix('query', z.object({
page: z.string().transform(Number).pipe(z.number().int().positive()),
limit: z.string().transform(Number).pipe(z.number().int().positive().max(50)),
})),
(c) => {
const { id } = c.req.valid('param' as never);
const { page, limit } = c.req.valid('query' as never);
return c.json({ userId: id, page, limit });
}
);
const paginationSchema = z.object({
page: z.string()
.default('1')
.transform(Number)
.pipe(z.number().int().positive()),
limit: z.string()
.default('10')
.transform(Number)
.pipe(z.number().int().positive().max(100)),
});
app.get('/items', zValidatorYelix('query', paginationSchema), handler);
const filterSchema = z.object({
status: z.enum(['active', 'inactive', 'pending']).optional(),
category: z.string().optional(),
minPrice: z.string()
.optional()
.transform((val) => val ? Number(val) : undefined)
.pipe(z.number().positive().optional()),
maxPrice: z.string()
.optional()
.transform((val) => val ? Number(val) : undefined)
.pipe(z.number().positive().optional()),
});
app.get('/products', zValidatorYelix('query', filterSchema), handler);
const sortSchema = z.object({
sortBy: z.enum(['name', 'price', 'createdAt']).default('createdAt'),
order: z.enum(['asc', 'desc']).default('desc'),
});
app.get('/products', zValidatorYelix('query', sortSchema), handler);

Query and path parameters are automatically documented in your OpenAPI spec:

import { YelixHono, openapi } from '@yelix/hono';
import { zValidatorYelix } from '@yelix/zod-validator';
import { z } from 'zod';
app.get(
'/users/:id',
openapi({
summary: 'Get User by ID',
}),
zValidatorYelix('param', z.object({
id: z.string().uuid(),
})),
zValidatorYelix('query', z.object({
include: z.enum(['posts', 'comments']).optional(),
})),
handler
);

The parameters will automatically appear in your OpenAPI documentation with their types and constraints!