API Reference
Current public surface of Bunderstack and its client packages
This page summarizes the stable public concepts. TypeScript remains the exact reference for generic details and adapter-specific types.
bunderstack(options)
function bunderstack<
TSchema,
TAccess,
TStorage,
TEnv,
TJobs,
TApi,
>(options: BunderstackConfig<...>): BunderstackBackend<BunderstackApp<...>>Required options:
schema: a record containing the application's Drizzle tables;database.adapter: one statically imported database adapter.
Major optional groups:
access: generated CRUD exposure, ownership, filters, sorting, and guards;auth/authResolver: Better Auth and custom session resolution;storage,email,env, andjobs: application facilities;api: your oRPC router, as an object or as(o) => router;middleware: oRPC middleware applied to every procedure in the graph;realtime: memory or Redis Publisher configuration;rateLimit,idempotency,background, andopenapi.
See Configuration for examples and defaults.
bunderstack() only declares the backend. backend.manifest is available
synchronously for Blueprint generation. Call await backend.start({ env }) to
materialize a production runtime, or await backend.test() to create an
isolated test fixture owned by the current lexical scope.
BunderstackApp
type BunderstackApp = {
handler(request: Request): Promise<Response>
db: DbFor<TSchema>
auth: AuthInstance
storage: StorageFacade
email: EmailFacade
env: ValidatedEnv<TEnv>
jobs: JobsFacade<TJobs>
realtime: RealtimeFacade<TSchema>
startWorker(options?): Promise<WorkerHandle>
runWorker(options?): Promise<void>
close(): Promise<void>
readonly status: LifecycleStatus
readonly signal: AbortSignal
readonly backgroundRunning: boolean
readonly $inferClient?: ClientTypeCarrier
}$inferClient is type-only and does not exist at runtime. Export
type App = typeof app for client inference.
API builder
const o = defineApi({ schema, env: envSchema })
o.public // no session resolution
o.protected // resolves the session, narrows context.user
o.webhook // public, preserves the exact raw body
o.middleware(fn) // a standalone middleware over ApiContextdefineApi infers TSchema and TEnv from the values it receives, so an
application never writes BunderstackApiBuilder<…> by hand. It reads nothing
at runtime and can be called at module scope. createApiBuilder<TSchema, TEnv>() remains available when you want to pass the generics explicitly.
The three bases are oRPC procedure builders with the shared Bunderstack error
contract and ApiContext:
type ApiContext = {
db: DbFor<TSchema>
env: ValidatedEnv<TEnv>
storage: StorageFacade
email: EmailFacade
jobs: JobsRuntimeFacade
realtime: RealtimeFacade<TSchema>
auth: AuthInstance
request: Request
resHeaders: Headers
getRawBody(): Promise<string>
getSession(): Promise<{
user: AccessUser | null
activeOrganizationId: string | null
}>
/** The already-resolved session, or undefined. Never starts a resolution. */
peekSession():
| { user: AccessUser | null; activeOrganizationId: string | null }
| undefined
}o.protected adds non-null context.user and the active organization session
to handlers. Inputs and optional outputs accept Standard Schema.
peekSession() exists for graph-wide middleware, which runs before
authentication. Use it for observability only, never for authorization — see
Middleware.
Helper exports
| Export | Purpose |
|---|---|
defineApi({ schema, env }) | The procedure builder, with generics inferred from values |
listSpec(table, options) | Input schema and handler for a list endpoint outside CRUD |
BunderstackError | Typed error for code outside a handler |
BunderstackDb<TSchema> | The database type for a helper parameter |
BunderstackTx<TSchema> | The transaction handle inside db.transaction |
Database and provisioning
Import one of libsql(), pglite(), bunSql(), or postgresJs() from its
bunderstack/* subpath. The schema dialect and adapter dialect must
match.
import { provision } from 'bunderstack/provision'
await provision(app, { force: false })Without a migration journal, provisioning uses the development schema push. With committed migrations, it applies pending migrations.
bunderstack/client
import { createClient, createLiveView } from 'bunderstack/client'
import { createRestClient } from 'bunderstack/client-rest'
import { useLiveView as useReactLiveView } from 'bunderstack/client-react'
import { createLiveStore } from 'bunderstack/client-solid'
import { liveStore } from 'bunderstack/client-svelte'
import { useLiveView as useVueLiveView } from 'bunderstack/client-vue'A zero-dependency typed RPC client and confirmed realtime LiveView with reactive store adapters for Solid, React, Svelte, and Vue. Use it for lightweight clients, mobile/native applications, or when you don't need TanStack Query.
bunderstack/query
function createClient<TApp>(options?: {
baseUrl?: string
fetch?: TransportFetch
queryClient?: QueryClient
}): BunderstackClient<TApp>The result contains the complete oRPC router utilities and file helpers:
api.posts.list.call(input)
api.posts.list.queryOptions({ input })
api.posts.create.mutationOptions(options)
api.customProcedure.call(input)
api.files.images.upload(file)
api.files.images.url(id, transforms)Other exports include createApiClient, syncRealtime, InferSchema,
InferSelect, InferInsert, and the list input helpers.
bunderstack/sync
function createSyncClient<TApp>(options: {
queryClient: QueryClient
baseUrl?: string
fetch?: TransportFetch
realtime?: boolean
}): BunderstackSyncClient<TApp>Each generated table exposes collection, table,
scopedCollection(options), and collectionByIds(ids). The client starts its
typed realtime iterator in the browser by default and keeps it disabled during
SSR.
bunderstack/start
createApiHandlers(app)
createIsomorphicFetch(options?)
getSessionUser(app, request)
createStartAuthClient(options?)
bunderstackStart<TApp>(options?)These adapters mount app.handler, resolve relative API URLs during SSR, and
create app-inferred clients without changing the server graph.
RealtimeFacade
interface RealtimeFacade<TSchema> {
readonly enabled: boolean
readonly transport: 'disabled' | 'memory' | 'redis'
publish<TTable extends SchemaTable<TSchema>>(
table: TTable,
action: 'create' | 'update' | 'delete',
record: InferSelectModel<TTable>,
): Promise<void>
}Generated writes publish automatically. Call publish() only for custom
writes performed through context.db or background jobs.
Storage
StorageFacade provides server-side upload, getUrl, and bucket-aware
operations. Browser clients receive upload, url, and delete helpers per
declared bucket. Upload and image-transform rules are documented in
Storage and Thumbnails.
interface EmailFacade {
send(message: EmailMessage): Promise<SentEmail>
}EmailMessage supports to, subject, HTML or text bodies, sender override,
reply-to, CC, and BCC. Configure the console, Resend, SMTP, or a custom adapter.
Testing (bunderstack/testing)
backend.test(options) produces an isolated test fixture with lexical async disposal
(await using). For a suite, backend.test.configure() removes repeated environment,
database, and seed boilerplate:
import { backend } from './bunderstack'
const createFixture = backend.test.configure({
database: { mode: 'temporary', schema: 'migrations' },
setup: async (fixture) => {
const identity = fixture.auth.mockSession({
id: 'alice',
email: '[email protected]',
name: 'Alice',
})
return { identity, client: fixture.client(identity) }
},
})
test('posts procedure works', async () => {
await using fixture = await createFixture()
const { client } = fixture.context
const result = await client.posts.create({ title: 'First post' })
expect(result.title).toBe('First post')
await fixture.jobs.runUntilIdle()
expect(fixture.email.sent).toHaveLength(1)
})TestFixture provides:
fixture.app: the materialized application runtime;fixture.context: the typed value returned by configuredsetup;fixture.defer(cleanup): LIFO async cleanup before application shutdown;fixture.auth: realsignUpEmail(),signInEmail(),getSession(),signOut(), andverifyEmail()flows, plus header-scopedmockSession()identities;fixture.client(identity?): inferred typed oRPC client callingapp.handlerdirectly in-process;fixture.jobs: deterministicrunNext()/runUntilIdle()execution andinspect()/pending()/failed()queue assertions;fixture.logs: captured internalentries,errors, andwarningswithclear();fixture.email: in-memory capture of sent emails atfixture.email.sent;fixture.storage: isolated test storage withfixture.storage.read(key).
configure() defaults and per-test options deep-merge env and database. Runtime
logs are captured by default; configure logs: 'inherit' to capture and forward to
the console, or logs: 'silent' to discard them.