Examples
Multi-Tenant SaaS
Organization-scoped permissions setup.
Use case: A SaaS app where users belong to organizations. Each user only sees data from their own orgs. Roles control what they can do within those orgs.
Backend
import { createEngine } from '@superapp/backend'
import { betterAuthProvider } from '@superapp/backend/auth/better-auth'
const auth = betterAuthProvider({
secret: process.env.AUTH_SECRET!,
userTable: {
table: 'main.users',
matchOn: { column: 'id', jwtField: 'id' },
},
})
const engine = createEngine({
connections: {
main: { type: 'postgres', url: process.env.PG_URL! },
},
auth,
// Enrich session with the user's org memberships
resolveSession: async (user, db) => {
const memberships = await db.main.members.findMany({
select: ['organization_id', 'role'],
where: { user_id: { $eq: user.id } },
})
return {
...user,
org_ids: memberships.map((m) => m.organization_id),
}
},
permissions: {
// Read orders — scoped to user's orgs
read_org_orders: {
table: 'main.orders',
operations: { select: true },
columns: ['id', 'amount', 'status', 'organization_id', 'created_at'],
filter: { organization_id: { $in: '$user.org_ids' } },
},
// Create orders — org_id must match user's orgs, amount must be positive
create_org_orders: {
table: 'main.orders',
operations: { insert: true },
columns: ['amount', 'status', 'organization_id'],
check: {
organization_id: { $in: '$user.org_ids' },
amount: { $gt: 0 },
status: { $eq: 'draft' },
},
preset: {
created_by: '$user.id',
created_at: '$now',
},
},
// Update orders — only within user's orgs
update_org_orders: {
table: 'main.orders',
operations: { update: true },
columns: ['amount', 'status'],
filter: { organization_id: { $in: '$user.org_ids' } },
check: {
amount: { $gt: 0 },
status: { $in: ['draft', 'active', 'cancelled'] },
},
preset: { updated_at: '$now' },
},
// Delete draft orders — only within user's orgs
delete_org_draft_orders: {
table: 'main.orders',
operations: { delete: true },
filter: {
organization_id: { $in: '$user.org_ids' },
status: { $eq: 'draft' },
},
},
},
roles: {
viewer: ['read_org_orders'],
editor: ['read_org_orders', 'create_org_orders', 'update_org_orders'],
admin: ['read_org_orders', 'create_org_orders', 'update_org_orders', 'delete_org_draft_orders'],
},
})How org scoping works:
resolveSessionfetches the user's org memberships and returnsorg_ids- Every permission uses
filter: { organization_id: { $in: '$user.org_ids' } }— injected as a WHERE clause - On inserts,
checkvalidates theorganization_idthe client sends matches one of the user's orgs - The client never filters by org — the server handles it automatically
Client
'use client'
import { useEffect, useState } from 'react'
import { useSession } from '@superapp/auth'
import { useDb } from '@/hooks/use-db'
import { desc } from 'drizzle-orm'
import * as schema from '@/generated/schema'
// Type is inferred from the Drizzle schema
type Order = Awaited<
ReturnType<ReturnType<typeof useDb>['query']['orders']['findMany']>
>[number]
export default function OrdersDashboard() {
const { data: session, isPending } = useSession()
const db = useDb()
const [orders, setOrders] = useState<Order[]>([])
const [loading, setLoading] = useState(true)
const loadOrders = () => {
if (!db) return
setLoading(true)
// No org filter needed — the server injects it automatically
db.query.orders
.findMany({
orderBy: desc(schema.orders.createdAt),
limit: 50,
})
.then(setOrders)
.finally(() => setLoading(false))
}
useEffect(loadOrders, [db])
// Create — backend validates org_id and enforces created_by
const createOrder = async (amount: number, organizationId: number) => {
await db!.insert(schema.orders).values({
amount,
status: 'draft',
organization_id: organizationId,
})
loadOrders()
}
// Update — backend scopes to user's orgs, validates status
const updateStatus = async (id: number, status: string) => {
await db!.update(schema.orders).set({ status }).where({ id })
loadOrders()
}
// Delete — backend only allows draft orders in user's orgs
const deleteOrder = async (id: number) => {
await db!.delete(schema.orders).where({ id })
loadOrders()
}
if (isPending) return <p>Loading session...</p>
if (!session) return <p>Please sign in to view orders.</p>
if (loading) return <p>Loading orders...</p>
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Amount</th>
<th>Status</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{orders.map((order) => (
<tr key={order.id}>
<td>{order.id}</td>
<td>${order.amount.toFixed(2)}</td>
<td>{order.status}</td>
<td>{new Date(order.created_at).toLocaleDateString()}</td>
<td>
<button onClick={() => updateStatus(order.id, 'cancelled')}>
Cancel
</button>
<button onClick={() => deleteOrder(order.id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
)
}