Type-Safe Filters & Pagination
Querystring Schemas
schema.querystring validates and coerces the URL search parameters, applying defaults and rejecting unknown filters.
What you'll learn
- Declare querystring with a JSON Schema object
- Coerce numeric and boolean query params
- Apply defaults so handlers stay clean
The querystring schema applies to whatever comes after ? in the URL. Like params, values arrive
as strings — coercion and defaults make them useful.
Pagination + Search
app.get('/posts', {
schema: {
querystring: {
type: 'object',
properties: {
page: { type: 'integer', minimum: 1, default: 1 },
pageSize: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
q: { type: 'string', maxLength: 200 },
sort: { type: 'string', enum: ['new', 'top', 'old'], default: 'new' },
},
},
},
handler: async (req) => {
const { page, pageSize, q, sort } = req.query;
return await db.posts.search({ page, pageSize, q, sort });
},
}); A request to /posts?page=2&pageSize=50 will arrive with req.query.page === 2 (a number), and
/posts alone will see page: 1, pageSize: 20, sort: 'new'.
Repeated Keys and Arrays
const querystring = {
type: 'object',
properties: {
tag: {
type: 'array',
items: { type: 'string' },
},
},
} as const; Fastify’s default querystring parser (fast-querystring) handles ?tag=js&tag=ts as
{ tag: ['js', 'ts'] }. Pair with the schema above for compile-time safety.
Reject Unknown Filters
Set additionalProperties: false to refuse queries with extra keys — handy for catching
deprecated filter names and obvious abuse.