bunderstackbunderstack/ docs

Framework Portability

Mount app.handler in TanStack Start, React SPA, Solid 2, Bun SSR, Next.js, and other modern TypeScript frameworks

app.handler(req: Request): Promise<Response> — every modern TypeScript framework knows this Web Standard shape.

Bunderstack runs in any environment supporting standard Web Requests and Responses. Below are the recommended integration patterns, configuration files, and deployment setups for each supported framework.


TanStack Start (Full-Stack SSR)

TanStack Start provides full-stack React with server-side rendering and streaming. The official bunderstack/start adapter handles API routing, SSR-aware data fetching, session lookup, and auth client configuration.

1. Installation

bun add bunderstack drizzle-orm valibot @libsql/client @tanstack/react-query @tanstack/react-start

2. Configuration (src/bunderstack.ts)

TanStack Start supports top-level await, allowing you to export app and its TypeScript type directly:

// src/bunderstack.ts
import { bunderstack } from 'bunderstack'
import { libsql } from 'bunderstack/libsql'
import { provision } from 'bunderstack/provision'
import { access } from './access'
import * as schema from './schema'

export const backend = bunderstack({
  schema,
  access,
  database: {
    adapter: libsql(),
    url: process.env.DATABASE_URL || 'file:./data.db',
  },
  auth: { emailAndPassword: { enabled: true } },
  realtime: true,
})

export const app = await backend.start()
export type App = typeof app
await provision(app)

3. API Catch-All Route (src/routes/api/$.tsx)

Mount the API handler on TanStack Start's catch-all route:

// src/routes/api/$.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createApiHandlers } from 'bunderstack/start'
import { app } from '~/bunderstack'

export const Route = createFileRoute('/api/$')({
  server: { handlers: createApiHandlers(app) },
})

4. Typed Client Setup (src/api.ts)

// src/api.ts
import { bunderstackStart } from 'bunderstack/start'
import type { App } from './bunderstack'

export const { createQueryClient, createApi } = bunderstackStart<App>()

Important: Do not name this file src/client.ts. TanStack Start reserves client.ts as its hydration entry point.

5. Package Scripts & Deployment

{
  "scripts": {
    "build": "vite build",
    "start": "bun .output/server/index.mjs"
  }
}

Running bunx bunderstack blueprint detects @tanstack/react-start and outputs framework: tanstack-start.


Solid 2 (Standalone Vite + Bun SSR)

Standalone Solid 2 (without SolidStart) uses a unified Bun server to handle static assets, perform server-side rendering, and delegate API requests directly to Bunderstack.

1. Installation

bun add bunderstack [email protected] @solidjs/[email protected] @solidjs/[email protected] drizzle-orm valibot @libsql/client @orpc/client
bun add -d vite [email protected]

2. Configuration (src/bunderstack.ts)

// src/bunderstack.ts
import { bunderstack } from 'bunderstack'
import { libsql } from 'bunderstack/libsql'
import { provision } from 'bunderstack/provision'
import * as schema from './schema'

export const backend = bunderstack({
  schema,
  database: {
    adapter: libsql(),
    url: process.env.DATABASE_URL || 'file:./data.db',
  },
  auth: { emailAndPassword: { enabled: true } },
  realtime: true,
})

export const app = await backend.start()
export type App = typeof app
await provision(app)

3. Unified HTTP Server (src/server.ts)

In production, src/server.ts routes all /api/* requests to app.handler, serves compiled client assets, and renders Solid 2 components via renderToString:

// src/server.ts
import { renderToString } from '@solidjs/web/server'
import { app } from './bunderstack'
import App from './App'

const port = Number(process.env.PORT) || 3000

Bun.serve({
  port,
  async fetch(req) {
    const url = new URL(req.url)

    // 1. Route API, auth, storage, jobs, and health check
    if (url.pathname.startsWith('/api/')) {
      return app.handler(req)
    }

    // 2. Serve static client assets from dist/client
    const filePath = `dist/client${url.pathname}`
    const file = Bun.file(filePath)
    if (await file.exists()) {
      return new Response(file)
    }

    // 3. Render Solid 2 SSR HTML
    const html = renderToString(() => <App url={url.pathname} />)
    return new Response(
      `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script type="module" src="/entry-client.js"></script>
  </head>
  <body>
    <div id="app">${html}</div>
  </body>
</html>`,
      { headers: { 'Content-Type': 'text/html; charset=utf-8' } },
    )
  },
})

console.log(`Solid 2 + Bunderstack running on port ${port}`)

4. Client Setup (src/api.ts)

// src/api.ts
import { createClient } from 'bunderstack/query'
import { QueryClient } from '@tanstack/solid-query'
import type { App } from './bunderstack'

export const queryClient = new QueryClient()
export const api = createClient<App>({ queryClient, baseUrl: '/api' })

5. Package Scripts & Deployment

{
  "bunderstack": {
    "entry": "src/bunderstack.ts"
  },
  "scripts": {
    "build": "vite build --outDir dist/client && vite build --ssr src/entry-server.tsx --outDir dist/server",
    "start": "bun src/server.ts"
  }
}

Running bunx bunderstack blueprint detects solid-js and sets framework: solid.


TanStack Router / React SPA (Vite)

In a Single Page Application (SPA) with Vite and React (or TanStack Router), the frontend bundle runs entirely in the browser and connects to a Bunderstack backend.

1. Configuration & Server (server.ts or src/bunderstack.ts)

// server.ts
import { bunderstack } from 'bunderstack'
import { libsql } from 'bunderstack/libsql'
import { provision } from 'bunderstack/provision'
import * as schema from './schema'

export const backend = bunderstack({
  schema,
  database: {
    adapter: libsql(),
    url: process.env.DATABASE_URL || 'file:./data.db',
  },
  auth: { emailAndPassword: { enabled: true } },
  realtime: true,
})

export const app = await backend.start()
export type App = typeof app
await provision(app)

const port = Number(process.env.PORT) || 3000
Bun.serve({
  port,
  async fetch(req) {
    const url = new URL(req.url)
    if (url.pathname.startsWith('/api/')) {
      return app.handler(req)
    }

    const file = Bun.file(`dist/${url.pathname}`)
    if (await file.exists()) return new Response(file)

    return new Response(Bun.file('dist/index.html'))
  },
})

2. Client Setup (src/api.ts)

// src/api.ts
import { createClient } from 'bunderstack/query'
import { QueryClient } from '@tanstack/react-query'
import type { App } from '../server'

export const queryClient = new QueryClient()
export const api = createClient<App>({
  queryClient,
  baseUrl: '/api',
})

3. Vite Proxy for Development (vite.config.ts)

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': 'http://localhost:3000',
    },
  },
})

Bun SSR (Pure Web Standards Bun Server)

For server-rendered applications using Bun with template literals, JSX, HTMX, Alpine.js, or Web Components without a full frontend framework.

1. Configuration (src/bunderstack.ts)

// src/bunderstack.ts
import { bunderstack } from 'bunderstack'
import { libsql } from 'bunderstack/libsql'
import { provision } from 'bunderstack/provision'
import * as schema from './schema'

export const backend = bunderstack({
  schema,
  database: {
    adapter: libsql(),
    url: process.env.DATABASE_URL || 'file:./data.db',
  },
  auth: { emailAndPassword: { enabled: true } },
  realtime: true,
})

export const app = await backend.start()
export type App = typeof app
await provision(app)

2. Server Entry (src/server.ts)

// src/server.ts
import { app } from './bunderstack'

const port = Number(process.env.PORT) || 3000

Bun.serve({
  port,
  async fetch(req) {
    const url = new URL(req.url)

    // Route API requests (oRPC, Auth, Storage, Jobs, Health)
    if (url.pathname.startsWith('/api/')) {
      return app.handler(req)
    }

    // Server-rendered HTML response
    return new Response(
      `<!DOCTYPE html>
<html>
  <head><title>Bun SSR App</title></head>
  <body>
    <h1>Welcome to Bunderstack on Bun SSR</h1>
  </body>
</html>`,
      { headers: { 'Content-Type': 'text/html; charset=utf-8' } },
    )
  },
})

3. Package Scripts & Deployment

{
  "scripts": {
    "build": "bun build ./src/client.ts --outdir ./dist",
    "start": "bun src/server.ts"
  }
}

Running bunx bunderstack blueprint detects a generic Bun project and assigns framework: bun-ssr.


Next.js (App Router)

1. Lazy Singleton (lib/bunderstack.ts)

// lib/bunderstack.ts
import { bunderstack } from 'bunderstack'
import { provision } from 'bunderstack/provision'
import * as schema from './schema'

const backend = bunderstack({
  schema,
  auth: { emailAndPassword: { enabled: true } },
})

let _app: Awaited<ReturnType<typeof backend.start>> | null = null

export async function getApp() {
  if (!_app) {
    _app = await backend.start()
    await provision(_app)
  }
  return _app
}

2. Route Handler (app/api/[...bunderstack]/route.ts)

// app/api/[...bunderstack]/route.ts
import { getApp } from '@/lib/bunderstack'

export async function GET(req: Request) {
  return (await getApp()).handler(req)
}
export const POST = GET
export const PATCH = GET
export const DELETE = GET

Next.js runs on Node.js. If connecting to a PostgreSQL database, install the Node driver: npm install postgres. When running under Bun, the native Bun.sql driver is selected automatically.


Other Web Standards Runtimes

Because app.handler implements the Web Standard (req: Request) => Promise<Response> signature, it integrates into any standard runtime in one line:

Hono

import { Hono } from 'hono'
import { app } from './bunderstack'

// Delegate all /api/* routes to Bunderstack
const server = createHonoApp()
server.all('/api/*', (c) => app.handler(c.req.raw))
export default server

Astro

// src/pages/api/[...all].ts
import type { APIRoute } from 'astro'
import { app } from '../../bunderstack'

export const ALL: APIRoute = ({ request }) => app.handler(request)

Deployment Blueprint Comparison

When generating a deployment contract via bunx bunderstack blueprint, Bunderstack identifies the application type and specifies it in bunderstack.blueprint.yaml:

Framework TypeBlueprint frameworkDetection RuleTypical build ScriptTypical start Script
TanStack Starttanstack-start@tanstack/react-start in dependenciesvite buildbun .output/server/index.mjs
Solid 2solidsolid-js or @solidjs/web in dependenciesvite build && vite build --ssr ...bun src/server.ts
Bun SSRbun-ssrStandard Bun scripts presentbun build ...bun src/server.ts
Custom / SPAcustomCustom specified frameworkCustom build commandCustom startup command

On this page