Documentation

Tenant isolation

Tenant identity comes from your authenticated backend. Attach the isolation column, pass tenantId on ask() and JWTs, and QueryPanel scopes generated SQL.

QueryPanel enforces tenant isolation at SQL generation time. You name the tenant column when you attach a database, then pass tenantId from your authenticated backend on every ask()call and every embed JWT. The model is instructed to filter by that column; the browser never chooses which tenant's data to load.

Attach with a tenant field

Set tenantFieldName to the column that partitions customer rows (often tenant_id). enforceTenantIsolation: true tells generation that a filter is required. tenantFieldType (for example String) helps the generator bind the parameter correctly.

attachPostgres with isolation
qp.attachPostgres("analytics", createPostgresClient(), {
  database: "analytics_db",
  description: "Customer analytics database",
  tenantFieldName: "tenant_id",
  tenantFieldType: "String",
  enforceTenantIsolation: true,
});

Pass tenantId from auth, not the client

Resolve the logged-in organization in your session or middleware, then pass that id. Do not accept tenantId from a query string, request body, or React state as the source of truth.

ask() and createJwt share the same tenant
const tenantId = req.user.tenantId; // from your auth layer

const result = await qp.ask("Show monthly revenue", {
  database: "analytics",
  tenantId,
});

const jwt = await qp.createJwt({ tenantId });

Related