superapp
AdvancedSecurity

Session Isolation

Every request gets an isolated database connection.

Chat in Claude

Each request runs queries against an isolated database connection acquired from a per-driver connection pool. Connections are sandboxed from other sessions and dangerous SQL statements are blocked before execution.

User A ──→ Connection from pool (permission-filtered, sandboxed)
User B ──→ Connection from pool (permission-filtered, sandboxed)
User C ──→ Connection from pool (permission-filtered, sandboxed)

How It Works

When a user makes a /data request, the engine acquires a database connection from the appropriate pool (Postgres, MySQL, or SQLite). The permission-filtered SQL is executed against this connection. After the query completes, the connection is returned to the pool for reuse.

What Is Blocked

The engine validates and rewrites all incoming SQL before execution. Dangerous operations are rejected at the permission layer:

Blocked CategoryExamples
Schema modificationCREATE TABLE, DROP TABLE, ALTER TABLE
Filesystem accessCOPY TO, pg_read_file(), LOAD_FILE()
System functionscurrent_setting(), pg_sleep(), system catalogs
Raw SQL executionOnly parameterized SQL from Drizzle Proxy is accepted
Unauthorized tablesAny table not covered by a permission is inaccessible

All data access goes through the engine's permission layer, which validates and modifies the parameterized SQL received from the Drizzle Proxy client. Users cannot bypass permission filters or access unauthorized tables.

Connection Pooling

Each database driver maintains its own connection pool to minimize latency:

const engine = createEngine({
  connections: {
    main: process.env.PG_URL!,        // postgres pool
    warehouse: process.env.MYSQL_URL!, // mysql pool
    local: './data/app.db',            // sqlite (single connection)
  },
  limits: {
    queryTimeout: 30_000,  // kill slow queries (ms)
  },
})
  • Each incoming request acquires a connection from the relevant pool
  • After the query completes, the connection is returned to the pool
  • If all connections are busy, new requests wait until one is available

Resource Limits

Each request is constrained to prevent a single user from consuming all server resources:

  • Time -- queryTimeout (default 30 seconds) applies to every query. Exceeded queries return 408 Request Timeout.
  • Rate -- rateLimitPerUser and rateLimitPerIP cap request frequency per minute.
  • Complexity -- maxRows, maxRelationDepth, and maxFilterNesting cap query complexity.
const engine = createEngine({
  limits: {
    queryTimeout: 30_000,     // kill slow queries (ms)
    rateLimitPerUser: 200,    // req/min per user
    rateLimitPerIP: 500,      // req/min per IP
    maxRows: 10_000,          // max rows a query can return
  },
})

Request Lifecycle

1. Request arrives with JWT
2. JWT validated → user identity extracted
3. Permissions evaluated → SQL validated and filtered
4. Connection acquired from pool
5. Permission-filtered SQL executed against the database
6. Results returned to client
7. Connection returned to pool

Connections do not persist any user state between requests. Each query starts from a clean slate with the permission-filtered SQL.

On this page