Skip to content
Ownfold
Esc
navigateopen⌘Jpreview
On this page

Hono

Integrate Hono on Node and Web runtimes with route mounting, authentication, CORS, compatible databases, ciphertext routes, and deployment guidance.

@ownfold/hono adapts Hono’s raw Web Request to Ownfold’s standard fetch handler. The adapter is small because Hono already uses Web Platform request and response types.

Database and identity are separate choices. Select both in the framework recipe builder, then pass the resulting VaultServer to the same Hono handler.

Install packages

pnpm add @ownfold/hono@beta @ownfold/server@beta hono

Add an authentication resolver and database adapter for your runtime. The build-tested Node example uses Better Auth and PostgreSQL in examples/hono-better-auth-postgres.

This is a complete backend integration. Add @ownfold/browser, @ownfold/fetch, or @ownfold/react only if a separate browser client needs them.

Create the server

import { betterAuthUserResolver } from "@ownfold/better-auth"
import { nodePostgresVaultAdapter } from "@ownfold/postgres/node-postgres"
import { createVaultServer } from "@ownfold/server"
import { auth } from "./auth"
import { pool } from "./database"

export const vaultServer = createVaultServer({
  adapter: nodePostgresVaultAdapter(pool),
  getUserId: betterAuthUserResolver(auth),
})

The resolver receives context.req.raw through the Ownfold handler. Session cookies, bearer headers, and other authentication inputs remain owned by the host auth system. The resolver returns only the stable user ID.

Mount the handler

import { createHonoVaultHandler } from "@ownfold/hono"
import { Hono } from "hono"
import { vaultServer } from "./vault-server"

const app = new Hono()

const ownfold = createHonoVaultHandler({
  server: vaultServer,
  isOriginAllowed: ({ request }) => {
    const origin = request.headers.get("origin")
    return origin === null || origin === process.env.APP_ORIGIN
  },
})

app.all("/api/ownfold/*", (context) => ownfold(context))

export default app

Register middleware before the route. Hono resolves matching handlers in registration order. A generic wildcard or early handler mounted first can consume the request before Ownfold sees it.

createHonoVaultHandler() returns a normal Hono Handler. It forwards context.req.raw and returns the handler’s Response; it does not read Hono variables or select an authenticated user from route params.

Browser transport

This section is optional. A backend-only Hono application can stop after mounting the handler.

import { createVaultClient } from "@ownfold/browser"
import { createFetchVaultTransport } from "@ownfold/fetch"

export const vault = createVaultClient({
  transport: createFetchVaultTransport({
    baseURL: "/api/ownfold",
  }),
})

For a separate frontend origin, set credentials: "include" and configure credentialed CORS on the Hono application. CORS must run before Ownfold routes:

import { cors } from "hono/cors"

app.use(
  "/api/ownfold/*",
  cors({
    origin: "https://app.example.com",
    allowMethods: ["GET", "POST", "OPTIONS"],
    allowHeaders: ["Content-Type"],
    credentials: true,
    maxAge: 600,
  }),
)

CORS response headers do not replace CSRF/origin validation. Keep isOriginAllowed, cookie SameSite policy, and the auth provider’s trusted-origin checks aligned.

Runtime compatibility

Hono runs on Node.js, Bun, Deno, Cloudflare Workers, and other Web runtimes. The Hono adapter is portable; the rest of your selected stack may not be.

Runtime Suitable adapters Important constraint
Node.js PostgreSQL, Drizzle, Prisma, SQLite Select drivers compatible with your Node version and deployment model.
Bun Web-compatible transports and supported DB drivers Verify ORM/driver and native-module support independently.
Cloudflare Workers Custom HTTP/database adapter or runtime-native driver node:sqlite, ordinary pg pools, and many Node auth packages are unavailable.
Deno Web-compatible custom adapters Do not assume Node package or native-addon compatibility.

Ownfold’s core contracts are runtime-neutral. Do not polyfill an incompatible database driver into an edge worker merely to reuse an official Node adapter; implement the narrow adapter contract for the runtime and run the shared compliance suite.

Ciphertext-only application routes

Ownfold coordination endpoints do not store application notes. Use a separate Hono route:

app.put("/api/notes/:id", async (context) => {
  const recordId = context.req.param("id")
  const body: unknown = await context.req.json()

  const validated = await vaultServer.validateEncryptedRecordWrite({
    request: context.req.raw,
    body,
    namespace: "notes",
    recordId,
  })

  if (validated.status === "error") {
    return context.json(
      { code: validated.error.code, message: validated.error.message },
      mapOwnfoldStatus(validated.error),
    )
  }

  const userId = await requireUserId(context.req.raw)
  await noteRepository.upsert({
    userId,
    recordId,
    encryptedPayload: validated.value.encryptedPayload,
  })

  return context.body(null, 204)
})

Derive namespace in server code and recordId from the canonical route. The browser uses the same values and authenticated user ID during encryption. Never persist a parallel plaintext title, preview, search field, or analytics property.

Error handling

The Ownfold handler returns structured protocol errors itself. A global app.onError() handles unexpected application failures, but it must not log request bodies, response bodies, Recovery Kit content, or decrypted values.

app.onError((error, context) => {
  logger.error({
    errorName: error.name,
    requestId: context.get("requestId"),
    route: context.req.path,
  })
  return context.json({ code: "INTERNAL_ERROR" }, 500)
})

Expected Ownfold failures remain normal HTTP responses and should not be rethrown into this handler.

Production checklist

  • Mount auth/session middleware before Ownfold if the provider requires it.
  • Mount CORS before Ownfold and use an exact allowlist.
  • Set body limits at the platform boundary; Ownfold additionally enforces its protocol limit.
  • Disable response caching for coordination and private-record routes.
  • Use HTTPS and secure cookies outside local development.
  • Close database pools during Node process shutdown.
  • Verify the deployed runtime supports the chosen auth and database packages.
  • Scrub request bodies from Hono logging middleware and observability exporters.

Troubleshooting

The handler returns 404 for every Ownfold path

Mount the wildcard as /api/ownfold/*, keep the transport base URL /api/ownfold, and ensure an earlier wildcard handler is not consuming the request.

Authentication works elsewhere but Ownfold returns 401

Verify the session cookie/header reaches context.req.raw and the auth middleware runs before the Ownfold route. Never inject a body userId as a workaround.

Cross-origin POST fails while GET succeeds

Check preflight ordering, allowed methods/headers, credentialed cookie settings, exact origin, and isOriginAllowed. CORS and origin validation must both pass.

A Worker deployment fails to bundle

Inspect the database and authentication packages, not only @ownfold/hono. Replace Node-only dependencies with runtime-native implementations behind Ownfold’s contracts.

Complete example

examples/hono-better-auth-postgres demonstrates a production-buildable Node service with Better Auth, PostgreSQL coordination, encrypted-note routes, connection limits, TLS, and graceful shutdown.

Last updated on August 4, 2026

Was this page helpful?