Request Limits
Rate limiting and query constraints.
The engine enforces limits on query complexity, request size, and request frequency to prevent abuse and protect downstream databases.
const engine = createEngine({
limits: {
maxRows: 10_000, // max rows a query can return
maxRelationDepth: 3, // orders → items → product = depth 3
maxFilterNesting: 5, // nested $and/$or/$not levels
maxFilterConditions: 20, // total conditions per query
maxRequestBodySize: '1mb', // max JSON body size
queryTimeout: 30_000, // kill slow queries (ms)
rateLimitPerUser: 200, // req/min per user
rateLimitPerIP: 500, // req/min per IP
},
})All Limits
| Limit | Default | Error | Description |
|---|---|---|---|
maxRows | 10_000 | 400 Limit exceeds maximum of 10000 | Cap on rows returned per query |
maxRelationDepth | 3 | 400 Relation depth exceeds maximum of 3 | How many levels deep relations can be nested |
maxFilterNesting | 5 | 400 Filter nesting exceeds maximum of 5 | How many levels deep $and/$or/$not can nest |
maxFilterConditions | 20 | 400 Filter has too many conditions (max 20) | Total filter conditions per query |
maxRequestBodySize | '1mb' | 413 Request body too large | Maximum JSON body size |
queryTimeout | 30_000 | 408 Query timed out after 30000ms | Maximum query execution time in milliseconds |
rateLimitPerUser | 200 | 429 Rate limit exceeded, retry after {n}s | Requests per minute per authenticated user |
rateLimitPerIP | 500 | 429 Rate limit exceeded, retry after {n}s | Requests per minute per IP address |
Query Complexity Limits
maxRows
Caps the limit parameter on findMany queries. If a client requests more rows than allowed, the engine returns an error instead of silently truncating:
// Client request
db.main.orders.findMany({ limit: 50_000 })
// → 400 Limit exceeds maximum of 10000maxRelationDepth
Prevents deeply nested relation joins that could generate expensive multi-table queries:
// Depth 1: orders → customers
// Depth 2: orders → customers → organizations
// Depth 3: orders → customers → organizations → members (max)
db.main.orders.findMany({
include: {
customer: {
include: {
organization: {
include: {
members: true, // depth 3 — allowed
},
},
},
},
},
})maxFilterNesting
Limits how deeply $and and $or conditions can be nested inside each other:
// Nesting level 1
{ $or: [
// Nesting level 2
{ $and: [
{ status: { $eq: 'active' } },
{ amount: { $gt: 100 } },
]},
{ status: { $eq: 'pending' } },
]}maxFilterConditions
Caps the total number of filter conditions across all nesting levels. This prevents clients from constructing queries with hundreds of $or branches.
Request Size Limits
maxRequestBodySize
Rejects requests with JSON bodies larger than the configured size. Accepts string values like '1mb', '500kb', or '2mb'.
Timeout
queryTimeout
Kills queries that run longer than the specified duration. The database connection is released and the client receives a 408 error. This protects against unoptimized queries or unexpected table scans.
Rate Limiting
Rate limits use a sliding window counter. When a client exceeds the limit, subsequent requests receive a 429 response with a Retry-After header.
rateLimitPerUser
Tracks requests by the sub claim in the JWT. Authenticated users share no state with other users.
rateLimitPerIP
Tracks requests by client IP address. This catches unauthenticated abuse and limits the blast radius of a compromised user token.
Both limits are applied independently. A request must pass both checks.
Overriding Per-Environment
Use different limits for development and production:
const isProd = process.env.NODE_ENV === 'production'
const engine = createEngine({
limits: {
// Tighter limits in production, relaxed for local development
maxRows: isProd ? 10_000 : 100_000,
rateLimitPerUser: isProd ? 200 : 10_000,
rateLimitPerIP: isProd ? 500 : 10_000,
queryTimeout: isProd ? 30_000 : 120_000,
},
})