zenstack-pagination-limiter
v0.1.5
Published
Automatically adds a default Take, enforces a maximum Take, and caps Skip for queries to prevent unsafe pagination
Readme
zenstack-pagination-limiter
A ZenStack runtime plugin that automatically enforces pagination limits on Kysely queries to prevent unsafe unbounded queries.
Features
- defaultTake — Automatically adds a
LIMITto queries that don't specify one - maxTake — Caps the maximum
LIMITvalue to prevent excessively large result sets - maxSkip — Caps the maximum
OFFSETvalue to prevent deep-pagination performance issues
Installation
npm install zenstack-pagination-limiterUsage
import PaginationLimiter from 'zenstack-pagination-limiter'
import { ZenStackClient } from '@zenstackhq/orm';
import schema from './schema.js';
const client = new ZenStackClient(schema, {
plugins: [
PaginationLimiter({
defaultTake: 20,
maxTake: 100,
maxSkip: 1000,
}),
],
});Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| defaultTake | number | -1 | The LIMIT value applied when a query has no limit. -1 means no limit (Kysely convention). |
| maxTake | number | — | The maximum allowed LIMIT value. Queries with a limit exceeding this value (or a negative limit) will be capped. |
| maxSkip | number | — | The maximum allowed OFFSET value. Queries with an offset exceeding this value will be capped. |
All options are optional. You can use them individually or in combination.
How It Works
The plugin uses Kysely's OperationNodeTransformer to intercept and transform SelectQueryNode objects before they are executed:
- If no
LIMITis set anddefaultTakeis configured, a default limit is added - If a
LIMITexceedsmaxTakeor is negative, it is capped tomaxTake - If an
OFFSETexceedsmaxSkip, it is capped tomaxSkip
Examples
Prevent queries without a limit
PaginationLimiter({ defaultTake: 50 })
// SELECT * FROM users → SELECT * FROM users LIMIT 50Cap maximum page size
PaginationLimiter({ maxTake: 100 })
// SELECT * FROM users LIMIT 9999 → SELECT * FROM users LIMIT 100
// SELECT * FROM users LIMIT -1 → SELECT * FROM users LIMIT 100
// SELECT * FROM users LIMIT 50 → SELECT * FROM users LIMIT 50 (unchanged)Prevent deep pagination
PaginationLimiter({ maxSkip: 5000 })
// SELECT * FROM users OFFSET 10000 → SELECT * FROM users OFFSET 5000
// SELECT * FROM users OFFSET 3000 → SELECT * FROM users OFFSET 3000 (unchanged)All options combined
PaginationLimiter({ defaultTake: 20, maxTake: 100, maxSkip: 1000 })
// SELECT * FROM users → SELECT * FROM users LIMIT 20
// SELECT * FROM users LIMIT 500 → SELECT * FROM users LIMIT 100
// SELECT * FROM users LIMIT 500 OFFSET 5000 → SELECT * FROM users LIMIT 100 OFFSET 1000Development
# Install dependencies
npm install
# Build
npm run build
# Run tests
npx vitest runLicense
MIT
