superapp
AdvancedSecurity

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: {
    maxLimit: 10_000,
    maxIncludeDepth: 3,
    maxFilterDepth: 5,
    maxFilterConditions: 20,
    maxRequestBodySize: '1mb',
    queryTimeout: 30_000,
    rateLimitPerUser: 200,
    rateLimitPerIP: 500,
  },
})

All Limits

LimitDefaultErrorDescription
maxLimit10_000400 Limit exceeds maximum of 10000Maximum rows returned per query
maxIncludeDepth3400 Include depth exceeds maximum of 3Maximum nesting depth for include (joins)
maxFilterDepth5400 Filter depth exceeds maximum of 5Maximum nesting of $and / $or conditions
maxFilterConditions20400 Filter has too many conditions (max 20)Maximum total filter conditions per query
maxRequestBodySize'1mb'413 Request body too largeMaximum JSON body size
queryTimeout30_000408 Query timed out after 30000msMaximum query execution time in milliseconds
rateLimitPerUser200429 Rate limit exceeded, retry after {n}sMaximum requests per minute per authenticated user
rateLimitPerIP500429 Rate limit exceeded, retry after {n}sMaximum requests per minute per IP address

Query Complexity Limits

maxLimit

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 10000

maxIncludeDepth

Prevents deeply nested 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
          },
        },
      },
    },
  },
})

maxFilterDepth

Limits nesting of $and and $or conditions:

// Depth 1
{ $or: [
  // Depth 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 DuckDB instance is terminated 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 engine = createEngine({
  limits: {
    maxLimit: process.env.NODE_ENV === 'production' ? 10_000 : 100_000,
    rateLimitPerUser: process.env.NODE_ENV === 'production' ? 200 : 10_000,
    rateLimitPerIP: process.env.NODE_ENV === 'production' ? 500 : 10_000,
    queryTimeout: process.env.NODE_ENV === 'production' ? 30_000 : 120_000,
  },
})

On this page