superapp
Advanced

Architecture

How the pieces fit together.

superapp is three npm packages: @superapp/backend (server), @superapp/db (data client), and @superapp/auth (auth client). The server owns all data access, auth, and permissions. The data client uses Drizzle Proxy to send parameterized SQL to the server, where permissions are applied before execution.

System Diagram

  @superapp/db (Data)          @superapp/auth (Auth)
  ┌────────────────────────┐   ┌──────────────────────────┐
  │  drizzle()             │   │  createAuth()            │
  │  Drizzle Proxy driver  │   │  AuthCard · AuthProvider │
  │  Sends SQL + params    │   │  useSession · UserButton │
  └────────────┬───────────┘   └────────────┬─────────────┘
               │  POST /data + JWT          │  POST /auth/*
               └────────────┬───────────────┘

  @superapp/backend (Server)
  ┌───────────────────────────────────┐
  │  HTTP Adapter (Hono/Express/Next) │
  │         │                         │
  │         ▼                         │
  │  Auth (better-auth / custom)      │
  │         │                         │
  │         ▼                         │
  │  Permissions (CASL)               │
  │  Validate SQL + inject filters    │
  │         │                         │
  │         ▼                         │
  │  DuckDB (in-process)              │
  └─────────┬─────────────────────────┘

    ┌───────┼───────┬───────┐
    ▼       ▼       ▼       ▼
  Postgres MySQL  SQLite   CSV

Three Packages

@superapp/backend (Server)

The backend is the single source of truth for all data access. It:

  • Receives parameterized SQL from the Drizzle Proxy client over HTTP.
  • Verifies JWTs through a pluggable auth provider.
  • Enforces permissions using CASL-based rules that validate the SQL, inject row filters, restrict columns, validate writes, and preset values.
  • Executes the permission-filtered SQL through DuckDB, which fans out to attached databases.
  • Stores metadata (sessions, roles, audit logs) in a Turso or local SQLite database via Drizzle ORM.
import { createEngine } from '@superapp/backend'
import { createHonoMiddleware } from '@superapp/backend/adapters/hono'

@superapp/db (Data Client)

The data client is real Drizzle ORM built on Drizzle Proxy. Drizzle builds parameterized SQL on the client, and the proxy driver sends SQL + params to the server. The data you get back is already filtered, restricted, and validated by the backend's permission engine. It:

  • Exposes standard Drizzle ORM query syntax that mirrors your schema.
  • Sends parameterized SQL + params to the server's /data endpoint via Drizzle Proxy.
  • Returns permission-filtered data — row filters, column restrictions, and write validations are applied transparently by the backend before results reach the client.
  • Generates TypeScript types from your server's /schema endpoint for full autocomplete.
import { drizzle } from '@superapp/db'

@superapp/auth (Auth Client)

The auth client handles session management and ships pre-built auth UI components. It uses better-auth by default, but supports custom adapters for any auth provider. It:

  • Manages JWT sessions — sign-in, sign-up, token refresh, sign-out.
  • Ships auth UI components (AuthCard, AuthProvider, UserButton) built with React.
  • Provides React hooks (useSession) for accessing the current session.
  • Supports custom adapters — swap the default better-auth adapter for Firebase, Auth0, Clerk, or any custom auth system.
import { createAuth } from '@superapp/auth'
import { useSession } from '@superapp/auth'
import { AuthCard, AuthProvider, UserButton } from '@superapp/auth/components'

Tech Stack

LayerTechnologyRole
Query engineDuckDBIn-process OLAP engine, ATTACH to external databases
HTTP frameworkHonoLightweight, edge-compatible HTTP routing
Metadata storeTurso + DrizzleSessions, roles, audit logs, admin configuration
Authenticationbetter-authJWT-based auth with session management
AuthorizationCASLAttribute-based permission evaluation
Admin UIReact + ViteVisual permission and connection management
Data client@superapp/dbDrizzle Proxy over HTTP, returns permission-filtered data
Auth client@superapp/authSession management, auth UI components (better-auth default, custom adapters)

Dependency Graph

  createEngine()
  ├── integrations[]  → Database providers     ──→ DuckDB ATTACH
  ├── connections{}   → Named DB configs       ──→ DuckDB ATTACH
  ├── auth            → AuthProvider
  │                     ├── verifyToken()
  │                     ├── findUser()
  │                     └── resolveSession()
  ├── permissions{}   → Permission definitions ──→ CASL abilities
  ├── roles{}         → Role → permission mappings
  ├── duckdb{}        → Pool config
  ├── limits{}        → Rate and query limits
  ├── audit{}         → Audit logging
  └── adapter         → HTTP adapter
                        ├── POST /data
                        ├── POST /auth/*
                        └── GET /schema

Request Lifecycle

Every query follows this exact path:

  1. HTTP -- Adapter receives POST /data with JWT in Authorization header. The body contains parameterized SQL + params from the Drizzle Proxy client.
  2. Auth -- verifyToken() validates the JWT signature. findUser() retrieves the user record. resolveSession() enriches with roles and org memberships.
  3. Permissions -- CASL evaluates which permissions apply based on the user's role. The engine validates the incoming SQL, injects WHERE filters, strips unauthorized columns, and for writes, validates data against check rules and injects preset values.
  4. DuckDB -- Executes the permission-filtered SQL against the attached database (Postgres, MySQL, SQLite, or CSV).
  5. Response -- Results are serialized to JSON and returned to the client.

No step can be skipped. There is no "bypass" mode in production. All SQL is parameterized (preventing injection), and the server validates and modifies every query before execution.

On this page