Introduction
A thin, secure data layer between your frontend and any database.
One library to connect your frontend to any database -- with authentication, row-level permissions, and type safety built in.
What is superapp?
superapp is two packages:
| Package | What it does |
|---|---|
@superapp/backend | Connects to Postgres, MySQL, SQLite, or CSV through DuckDB. Handles auth, enforces row-level permissions, runs queries. |
@superapp/db | Drizzle ORM client with an HTTP driver. Full type safety, auth UI components, zero server dependencies. |
Frontend (React, Next.js)
│
▼
@superapp/db (Drizzle ORM)
│
│ HTTP + JWT
▼
@superapp/backend
│
┌───┼───────────────┐
│ │ │
▼ ▼ ▼
Auth Permissions Query Builder
│
▼
DuckDB
│
┌─────┬───────┼────────┐
▼ ▼ ▼ ▼
Postgres MySQL SQLite CSVHow it works
- Define your server -- connect databases, configure auth, declare permissions
- Generate types -- the CLI introspects your schema and outputs Drizzle table definitions
- Query from the frontend -- use standard Drizzle ORM syntax, permissions are enforced automatically
Quick Example
Server (server.ts):
import { createEngine } from '@superapp/backend'
import { betterAuthProvider } from '@superapp/backend/auth/better-auth'
import { postgresProvider } from '@superapp/backend/integrations/postgres'
import { createHonoMiddleware } from '@superapp/backend/adapters/hono'
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
const engine = createEngine({
integrations: [postgresProvider],
connections: {
main: { type: 'postgres', url: process.env.PG_URL! },
},
auth: betterAuthProvider({ secret: process.env.AUTH_SECRET! }),
permissions: {
view_own_orders: {
table: 'main.orders',
operations: { select: true },
columns: ['id', 'amount', 'status', 'created_at'],
filter: { customer_id: { $eq: '$user.id' } },
},
},
roles: {
viewer: ['view_own_orders'],
},
})
const app = new Hono()
app.route('/', createHonoMiddleware(engine))
serve({ fetch: app.fetch, port: 3001 })Client (lib/superapp.ts):
import { drizzle } from '@superapp/db'
import { createAuth } from '@superapp/db/auth'
import * as schema from '../generated/schema'
export const authClient = createAuth('http://localhost:3001')
export function createDb(token: string) {
return drizzle({
connection: 'http://localhost:3001',
token,
schema,
})
}Query (anywhere in your frontend):
import { eq, desc } from 'drizzle-orm'
const orders = await db.select()
.from(schema.orders)
.where(eq(schema.orders.status, 'active'))
.orderBy(desc(schema.orders.createdAt))
.limit(50)The filter: { customer_id: { $eq: '$user.id' } } in the server config injects a WHERE clause into every query -- the client never sees it, and cannot bypass it.
What's Next
- Installation -- Install the packages and set up your environment.
- Quick Start -- Get running in 5 minutes.