Examples
Orders Dashboard
Full CRUD example with auth, validation, and row-level security.
Use case: A sales team app where each rep creates and manages their own orders. Reps can only see, edit, and delete their own orders. Admins can view all orders.
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,
permissions: {
// Rep sees only orders they created
read_own_orders: {
table: 'main.orders',
operations: { select: true },
columns: ['id', 'amount', 'status', 'created_by', 'created_at'],
filter: { created_by: { $eq: '$user.id' } },
},
// Admin sees all orders
read_all_orders: {
table: 'main.orders',
operations: { select: true },
columns: ['id', 'amount', 'status', 'created_by', 'created_at'],
},
// Rep creates orders — ownership is enforced, amount must be positive,
// new orders can only start as draft
create_own_orders: {
table: 'main.orders',
operations: { insert: true },
columns: ['amount', 'status'],
check: {
amount: { $gt: 0 },
status: { $eq: 'draft' },
},
preset: {
created_by: '$user.id',
created_at: '$now',
},
},
// Rep updates only their own orders — amount stays positive,
// status transitions are restricted
update_own_orders: {
table: 'main.orders',
operations: { update: true },
columns: ['amount', 'status'],
filter: { created_by: { $eq: '$user.id' } },
check: {
amount: { $gt: 0 },
status: { $in: ['draft', 'active', 'cancelled'] },
},
preset: { updated_at: '$now' },
},
// Rep can only delete their own draft orders
delete_own_draft_orders: {
table: 'main.orders',
operations: { delete: true },
filter: {
created_by: { $eq: '$user.id' },
status: { $eq: 'draft' },
},
},
},
roles: {
sales_rep: [
'read_own_orders',
'create_own_orders',
'update_own_orders',
'delete_own_draft_orders',
],
admin: [
'read_all_orders',
'create_own_orders',
'update_own_orders',
'delete_own_draft_orders',
],
},
})How it works:
filter— injects a WHERE clause. A rep querying orders automatically getsWHERE created_by = ?— they never see other reps' orders.check— validates incoming data. Sendingamount: -5orstatus: 'shipped'is rejected with403before any SQL runs.preset— auto-setscreated_byand timestamps. The client cannot override these — ownership is enforced server-side.
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 — no manual interface needed
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)
// Fetch orders — backend scopes to current user automatically
const loadOrders = () => {
if (!db) return
setLoading(true)
db.query.orders
.findMany({
orderBy: desc(schema.orders.createdAt),
limit: 50,
})
.then(setOrders)
.finally(() => setLoading(false))
}
useEffect(loadOrders, [db])
// Create — backend enforces created_by and validates amount > 0
const createOrder = async (amount: number) => {
await db!.insert(schema.orders).values({ amount, status: 'draft' })
loadOrders()
}
// Update — backend only allows own orders, validates status transitions
const updateStatus = async (id: number, status: string) => {
await db!.update(schema.orders).set({ status }).where({ id })
loadOrders()
}
// Delete — backend only allows own draft orders
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>
)
}