superapp
Client

Overview

Drizzle ORM for the frontend — real Drizzle with a proxy 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 is built on Drizzle Proxy — Drizzle ORM builds parameterized SQL on the client, and the proxy driver sends SQL + params to the superapp backend over HTTP. The server validates your JWT, applies row-level permissions to the SQL, and executes against your database via DuckDB. The data returned to the client has already been filtered and restricted by the permission engine.

  Your Frontend                superapp Backend                  Database
  ─────────────                ────────────────                  ────────
  db.select(...)               Drizzle builds SQL
    │                          on the client

  Proxy sends     ── SQL ───▶  Verify JWT
  SQL + params       + params  Who is this user?


                               Apply permissions
                               • Inject WHERE user_id = ?
                               • Restrict visible columns
                               • Validate write operations


                               Execute modified  ─── query ────▶  Postgres
                               SQL via DuckDB                     MySQL
                               Return filtered   ◀── results ──  SQLite
                                  results                         CSV

  Receive scoped  ◀── JSON ──       │
  data only

You 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 client

Available Methods

MethodDescription
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

ImportPathDescription
drizzle@superapp/dbCreate a Drizzle client with proxy driver
eq, gt, desc, ...drizzle-ormStandard Drizzle filter and sort operators
schema./generated/schemaAuto-generated Drizzle schema from your database
createAuth@superapp/authAuth client for session management
useSession@superapp/authReact hook for current session
AuthProvider@superapp/auth/componentsRoot layout wrapper for auth context
AuthCard@superapp/auth/componentsPre-built sign-in/sign-up/forgot-password UI
UserButton@superapp/auth/componentsNavbar dropdown with avatar and sign out

What's Next

On this page