superapp
Getting Started

Quick Start

Get running in 5 minutes.

From zero to querying your database with auth and permissions in five steps.

The Fastest Way

Scaffold a complete project with one command:

npx @superapp/backend create-app my-app

This creates a monorepo with a configured backend, a Next.js frontend, environment files, and example permissions. Run npm run dev and you're live.

If you prefer to set things up manually, follow the steps below.

Manual Setup

Step 1: Create the Engine Config

Create server.ts in your backend project:

import { createEngine } from '@superapp/backend'
import { betterAuthProvider } from '@superapp/backend/auth/better-auth'
import { postgresProvider } from '@superapp/backend/integrations/postgres'

const auth = betterAuthProvider({
  secret: process.env.AUTH_SECRET!,
  userTable: {
    table: 'main.users',
    matchOn: { column: 'id', jwtField: 'id' },
    columns: ['id', 'email', 'name'],
  },
})

export const engine = createEngine({
  superapp_db: './superapp.db',
  integrations: [postgresProvider],
  connections: {
    main: { type: 'postgres', url: process.env.PG_URL! },
  },
  auth,
  masterKey: process.env.SUPERAPP_MASTER_KEY!,
  permissions: {
    view_own_orders: {
      name: 'View own orders',
      table: 'main.orders',
      operations: { select: true },
      columns: ['id', 'amount', 'status', 'customer_id', 'created_at'],
      filter: {
        organization: {
          members: { user_id: { $eq: '$user.id' } },
        },
      },
    },
  },
  roles: {
    viewer: ['view_own_orders'],
  },
})

Step 2: Start the Server

Add the HTTP adapter below your engine config in the same file, or import the engine into a separate entry point:

import { createHonoMiddleware } from '@superapp/backend/adapters/hono'
import { Hono } from 'hono'
import { serve } from '@hono/node-server'

const app = new Hono()
app.route('/', createHonoMiddleware(engine))

serve({ fetch: app.fetch, port: 3001 }, (info) => {
  console.log(`@superapp/backend running on http://localhost:${info.port}`)
})

Not using Hono? superapp ships adapters for Next.js, Express, and a generic handler for Cloudflare Workers, Deno, and Bun.

Step 3: Set Up the Client SDK

In your frontend project, create lib/superapp.ts:

import { drizzle } from '@superapp/db'
import { createAuth } from '@superapp/auth'
import * as schema from '../generated/schema'

const SUPERAPP_URL = process.env.NEXT_PUBLIC_SUPERAPP_URL!

export const authClient = createAuth(SUPERAPP_URL)

export function createDb(token: string) {
  return drizzle({
    connection: SUPERAPP_URL,
    token,
    schema,
  })
}

Generate your SuperAppSchema types by running npx @superapp/backend generate against your running server. This introspects your database and outputs a TypeScript schema file.

Step 4: Add Auth

Wrap your app with AuthProvider and drop in AuthCard for sign-in/sign-up:

// app/layout.tsx
import { AuthProvider } from '@superapp/auth/components'
import { authClient } from '@/lib/superapp'
import { useRouter } from 'next/navigation'
import Link from 'next/link'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const router = useRouter()

  return (
    <html lang="en">
      <body>
        <AuthProvider
          authClient={authClient}
          navigate={router.push}
          replace={router.replace}
          Link={Link}
          onSessionChange={() => router.refresh()}
        >
          {children}
        </AuthProvider>
      </body>
    </html>
  )
}
// app/auth/[[...slug]]/page.tsx
import { AuthCard } from '@superapp/auth/components'

export default function AuthPage() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <AuthCard />
    </div>
  )
}

This gives you /auth/sign-in, /auth/sign-up, and /auth/forgot-password — fully functional, no form code.

Step 5: Query Data

Create a hook to get a typed database client, then query:

// hooks/use-db.ts
import { useMemo } from 'react'
import { useSession } from '@superapp/auth'
import { createDb } from '@/lib/db'

export function useDb() {
  const { data: session } = useSession()
  return useMemo(
    () => session?.token ? createDb(session.token) : null,
    [session?.token],
  )
}
// app/dashboard/page.tsx
'use client'

import { useEffect, useState } from 'react'
import { useDb } from '@/hooks/use-db'
import { eq, desc } from 'drizzle-orm'
import * as schema from '@/generated/schema'

export default function DashboardPage() {
  const db = useDb()
  const [orders, setOrders] = useState<any[]>([])

  useEffect(() => {
    if (!db) return
    db.select()
      .from(schema.orders)
      .where(eq(schema.orders.status, 'active'))
      .orderBy(desc(schema.orders.createdAt))
      .limit(50)
      .then(setOrders)
  }, [db])

  if (!db) return <p>Please sign in.</p>

  return (
    <ul>
      {orders.map((o) => (
        <li key={o.id}>Order #{o.id} - ${o.amount} - {o.status}</li>
      ))}
    </ul>
  )
}

What You Just Built

Browser                       Backend                        Database
  |                              |                              |
  |-- POST /auth/sign-in ------>|                              |
  |<-------- JWT token ---------|                              |
  |                              |                              |
  |-- POST /data -------------->|                              |
  |   (JWT + Drizzle query)     |                              |
  |                              |-- Verify JWT                |
  |                              |-- Resolve session ($user)   |
  |                              |-- Check permissions         |
  |                              |-- Inject WHERE filters      |
  |                              |                              |
  |                              |-- Execute SQL via DuckDB -->|
  |                              |<-------- Results -----------|
  |                              |                              |
  |<------ Typed response ------|                              |

What's Next

  • Backend Configuration — Full reference for createEngine options, connections, DuckDB tuning, and security settings.
  • Client Queries — All query methods: db.select(), db.insert(), db.update(), db.delete().
  • Permissions — Row-level filters, column restrictions, write checks, and preset values.

On this page