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.
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.
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
- ask() — natural language to tenant-scoped SQL.
- Mint a tenant JWT — the embed token carries the same tenantId.
- Tenant isolation for customer-facing analytics — product-level explainer.