Overview
Drizzle ORM for the frontend — real Drizzle with an HTTP driver that returns permission-filtered data.
@superapp/db gives your frontend real Drizzle ORM connected to any database through the superapp backend. Same API you already know — the data you get back is already filtered, restricted, and validated by the backend's permission engine.
How It Works
The client uses a Drizzle HTTP driver plugin that sends queries to the superapp backend. The server validates your JWT, applies row-level permissions, and executes against your database. The data returned to the client has already been filtered and restricted by the permission engine — the client consumes Drizzle ORM, but with all data scoped to the user's configured permissions.
Your Frontend superapp Backend Database
───────────── ──────────────── ────────
db.select(...) ── JSON ──▶ Verify JWT
Who is this user?
│
▼
Apply permissions
• Add WHERE user_id = ?
• Restrict visible columns
• Validate write operations
│
▼
Build & run SQL ─── query ────▶ Postgres
MySQL
Return filtered ◀── results ── SQLite
results CSV
│
Receive scoped ◀── JSON ── │
data onlyYou write normal Drizzle queries. The backend ensures each user only sees their own data.
Quick Example
import { drizzle } from '@superapp/db'
import { eq, desc } from 'drizzle-orm'
import * as schema from './generated/schema'
// The token ties this Drizzle instance to a specific user.
// Every query through this `db` will only return that user's data.
const db = drizzle({
connection: 'http://localhost:3001',
token: session.token, // ← JWT identifies the logged-in user
schema,
})
// This looks like a normal Drizzle query — no user_id filter needed.
// The backend reads the JWT, resolves the user, and automatically
// scopes results so this user can only see their own orders.
const orders = await db.select()
.from(schema.orders)
.where(eq(schema.orders.status, 'active'))
.orderBy(desc(schema.orders.createdAt))
.limit(10)
// If Alice is logged in: returns only Alice's active orders
// If Bob is logged in: returns only Bob's active orders
// Same code, different data — enforced by the backend, not the clientAvailable Methods
| Method | Description |
|---|---|
db.select() | Select rows with filtering, sorting, joins, and pagination |
db.query.*.findMany() | Relational queries with eager loading |
db.query.*.findFirst() | Find a single record with relations |
db.insert() | Insert one or more rows |
db.update() | Update rows matching a condition |
db.delete() | Delete rows matching a condition |
db.select({ count: count() }) | Count rows |
db.select({ total: sum() }) | Aggregations (sum, avg, min, max) |
Imports Summary
| Import | Path | Description |
|---|---|---|
drizzle | @superapp/db | Create a Drizzle client with HTTP driver |
eq, gt, desc, ... | drizzle-orm | Standard Drizzle filter and sort operators |
schema | ./generated/schema | Auto-generated Drizzle schema from your database |
createAuth | @superapp/auth | Auth client for session management |
useSession | @superapp/auth | React hook for current session |
AuthProvider | @superapp/auth/components | Root layout wrapper for auth context |
AuthCard | @superapp/auth/components | Pre-built sign-in/sign-up/forgot-password UI |
UserButton | @superapp/auth/components | Navbar dropdown with avatar and sign out |
What's Next
- Setting Up the Client — Configure
drizzle()with your backend URL and schema. - Drizzle Compatibility — Side-by-side comparison with standard Drizzle.
- Auth Setup — Configure authentication in your frontend.