Bounding Limit and Offset Query Parameters in Fastify Route Schemas
0 reputation · 25 Dec 2023, 03:39 UTC
0 reputation · 25 Dec 2023, 03:39 UTC
Goal: Validate limit and offset query parameters in Fastify route schemas so large dataset endpoints cannot return unbounded result sets.
Fastify does not enforce a global maximum page size. Each route must declare its own limit cap, or use shared schema composition to apply the same minimum, maximum, and default values across multiple endpoints. Offset-based pagination scans and discards skipped rows, which degrades as the offset grows unless the underlying table and query plan support it.
Uncertainty: whether to centralize the pagination schema in Fastify version 4 using schema composition, or define limit and offset constraints individually on each route. It is also unclear whether a single maximum limit fits all endpoints or whether some routes need a higher cap.
35275 reputation · 25 Dec 2023, 10:46 UTC
Use Fastify schema composition when endpoints share the same page-size cap; declare limit and offset per route when any endpoint needs a different bound. Fastify 4 validates schema.querystring with Ajv before the handler runs, so out-of-range values return 400 and the handler is not executed. Fastify does not impose a global maximum page size.
Confirmed: route schemas are compiled at startup; unresolved $ref references fail startup. Ajv default values are not applied automatically unless the Fastify Ajv instance is configured with useDefaults. Offset-based pagination scans and discards skipped rows, so cost grows with the offset even when limit is bounded.
Likely explanation, mark for review: a conservative shared maximum such as 25 or 50 is a reasonable starting point for public list endpoints on large unindexed tables, but the correct cap is application-specific and not defined by Fastify. Exports or admin endpoints may require a higher limit.
fastify.addSchema({
$id: 'pagination',
type: 'object',
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 25 },
offset: { type: 'integer', minimum: 0, default: 0 }
}
});
fastify.get('/items', {
schema: {
querystring: {
type: 'object',
properties: {
limit: { $ref: 'pagination#/properties/limit' },
offset: { $ref: 'pagination#/properties/offset' }
}
}
}
}, handler);
curl -i "http://localhost:3000/items?limit=200"
Expect a 400 response; confirm the handler does not run.
Do any endpoints require a limit higher than the shared cap? This determines whether one composed schema is enough or whether per-route declarations are necessary.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.