Framework adapters
Choose a full-stack or backend-only Ownfold adapter and mount the same framework-independent vault server.
Ownfold framework packages are server adapters, not UI kits. They translate the host framework’s
request boundary to one VaultServer; none requires React unless the host application independently
chooses the React bindings.
How adapters fit
your authentication your database
│ │
▼ ▼
getUserId VaultAdapter
╲ ╱
╲ ╱
VaultServer
│
framework adapter
│
your existing routes
Validation, authorization, ownership, rate limits, audit hooks, state transitions, and storage semantics live below the framework layer.
Full-stack frameworks
| Framework | Package | Server boundary | Guide |
|---|---|---|---|
| Next.js App Router | @ownfold/next |
Route handlers using Web Request and Response |
Next.js |
| TanStack Start | @ownfold/tanstack-start |
Splat server route using Web requests | TanStack Start |
These guides show an optional browser client because the frameworks can host both sides. Their
server adapters still work independently of @ownfold/react.
Backend frameworks
| Framework/runtime | Package | Bridge | Guide |
|---|---|---|---|
| Raw Node.js / Express | @ownfold/node |
Node request/response to Web handler | Node.js and Express |
| Fastify | @ownfold/fastify |
Raw Node objects through @ownfold/node |
Fastify |
| Hono | @ownfold/hono |
Native Web request | Hono |
| Elysia | @ownfold/elysia |
Native Web request | Elysia |
| tRPC v11 | @ownfold/trpc |
Typed procedures calling VaultServer |
tRPC |
| Fetch-compatible runtime | @ownfold/fetch |
Canonical Request => Response handler |
Fetch handler |
All are suitable for API-only applications. Start with the backend quickstart if no frontend exists.
Choose by runtime
| Deployment | Recommended choice |
|---|---|
| Node.js with Express | @ownfold/node |
| Node.js with Fastify | @ownfold/fastify |
| Bun with Elysia | @ownfold/elysia |
| Node, Bun, Deno, or edge with Hono | @ownfold/hono |
| Cloudflare Workers | Hono or @ownfold/fetch, plus runtime-compatible storage/auth |
| Custom server exposing Web requests | @ownfold/fetch |
| Existing tRPC v11 API | @ownfold/trpc |
Framework support and database support are separate. For example, Hono can run on Workers, but
node:sqlite and an ordinary pg pool cannot. Select a database adapter compatible with the actual
runtime.
Shared server setup
Choose one database and one identity. They are independent of the framework adapter:
import { DatabaseSync } from "node:sqlite"
import { SqliteVaultAdapter } from "@ownfold/sqlite"
export const adapter = new SqliteVaultAdapter(new DatabaseSync("ownfold.db"))
import { nodePostgresVaultAdapter } from "@ownfold/postgres/node-postgres"
import { Pool } from "pg"
export const adapter = nodePostgresVaultAdapter(
new Pool({ connectionString: process.env.DATABASE_URL }),
)
import { drizzleVaultAdapter } from "@ownfold/drizzle"
import { db } from "./db"
export const adapter = drizzleVaultAdapter(db)
import { prismaVaultAdapter } from "@ownfold/prisma"
import { prisma } from "./prisma"
export const adapter = prismaVaultAdapter(prisma)
export const getUserId = betterAuthUserResolver(auth)
export const getUserId = authJsUserResolver(async () => auth())
export const getUserId: OwnfoldUserResolver = async ({ request }) =>
(await sessions.verify(request.headers))?.user.id ?? null
export const getUserId = singleUserResolver("local-owner")
Private single-user processes only. This resolver does not authenticate callers.
Every guide begins with the same server:
import { createVaultServer } from "@ownfold/server"
export const vaultServer = createVaultServer({
adapter,
getUserId,
})
Authentication may come from Better Auth, Auth.js, or a custom resolver. Storage may use SQLite, PostgreSQL, Drizzle, Prisma, or a custom adapter.
Application records are separate
Framework adapters expose Ownfold coordination operations. Your application still owns record
routes and tables for messages, documents, files, or domain data. Validate encrypted writes with
VaultServer.validateEncryptedRecordWrite; do not add plaintext or decryption to the coordination
router.
Build a custom adapter
If your framework accepts Web Request and returns Web Response, delegate directly:
import { createVaultFetchHandler } from "@ownfold/fetch"
const handle = createVaultFetchHandler({ server: vaultServer })
export const frameworkHandler = (context: { request: Request }) => handle(context.request)
Node-style frameworks should reuse createNodeVaultHandler rather than reimplementing streaming,
header conversion, response writing, or size limits. See the
adapter architecture and run the shared tests before publishing an adapter.