Deployment
BuiltOuter.handle(request: Request): Promise<Response> is a plain Fetch API handler, so Outer mounts as the server entry for any framework that speaks fetch — Nitro, Hono, H3, Next.js API Routes, etc.
The zero-infra default: pglite()
For persistent-hosting deployments (VPS, Coolify), import the pglite() helper from the /pglite subpath:
import { Outer } from "@outerjs/server";
import { pglite } from "@outerjs/server/pglite";
new Outer({ db: pglite() }); // or pglite({ dataDir: "..." }), defaults to <cwd>/.outer/pglite
PGlite is real Postgres, running embedded and writing to local disk — no external infra to run. This is the path to reach for first; it's what makes Outer deployable to a VPS/Coolify box with nothing else to provision. Splitting it into a subpath keeps PGlite's WASM out of deploy bundles for platforms where it's dead weight (Cloudflare Workers, Vercel Functions).
Because PGlite writes to local disk, the host needs a persistent, writable filesystem across requests. This works on a VPS, Coolify, or any long-lived Node process — it does not work on serverless/edge platforms unless you swap in a different dialect.
Custom dialects for serverless/edge
For platforms without a persistent filesystem (Vercel Functions, Cloudflare Workers), or to point at an existing database, pass any Kysely Dialect directly:
import { D1Dialect } from "kysely-d1"; // or PostgresDialect, MysqlDialect, kysely-durable-objects, etc.
new Outer({
db: { dialect: new D1Dialect({ database: env.DB }), kind: "sqlite" },
});
kind tells Outer which SQL dialect family to generate DDL for (column types like serial/jsonb/uuid don't exist in SQLite, so they're remapped) and which constraint-error codes to recognize when turning DB errors into CONFLICT/BAD_REQUEST responses.
Currently supported:
"postgres"— PGlite, or any network Postgres viaPostgresDialect"sqlite"— Cloudflare D1, Durable Objects viakysely-durable-objects, libSQL/Turso, etc.
Kysely also ships mysql/mssql dialects, and Better Auth supports them — but Outer doesn't generate correct DDL for them yet, so kind is deliberately typed to just the two verified options.
Verified templates
templates/cloudflare— Cloudflare Workers with Durable Objectstemplates/vercel-neon— Vercel Functions with Neon Postgres
Both ship with a deploy script:
$ npm run deploy
# templates/cloudflare -> wrangler deploy
# templates/vercel-neon -> vercel deploy --prod
Embedding in a host framework
The handler itself is host-agnostic. Export whatever shape the host expects and delegate to outer.handle:
// e.g. Nitro server entry (see templates/ilha)
export default { fetch: (req: Request) => outer.handle(req) };
Use .middleware() to pull the host runtime's own utilities into context, alongside context.db/context.auth, so they're available in every .procedure():
import { useStorage } from "nitro/storage";
import { runTask } from "nitro/task";
const outer = new Outer(...)
.schema(v1_0)
.auth({ secret: useRuntimeConfig().authSecret })
.middleware(async ({ context, next }) => {
const kv = useStorage();
return next({ context: { kv, runTask } });
})
.procedure("foo", (base) =>
base.handler(async ({ context }) => {
await context.kv.setItem("foo", "bar");
return { foo: await context.kv.getItem("foo") };
}),
)
.build();
This is the same pattern regardless of host — swap nitro/storage/nitro/task for whatever the framework provides (Cloudflare bindings, Next.js headers(), etc.).
HTTP routes
| Method | Path | Handler |
|---|---|---|
GET | /openapi.json | OpenAPI 3.x spec (only mounted when .openapi({ enabled: true }) was called) |
ALL | /api/auth/** | Better Auth handler (only mounted when .auth() was called) |
ALL | /rpc/** | oRPC handler (prefix /rpc) |
Roadmap
Alpha focuses on persistent-hosting deployments (VPS, Coolify) with pglite() as the recommended default. One thing is planned next:
- Admin dashboard/UI — comparable to PocketBase's dashboard or Supabase Studio. Should expose: table data browser with CRUD, user/session management, and migration status. Planned as a separate
outer-adminpackage served at/adminwhen enabled.
Serverless/edge support is no longer blocked — db: { dialect, kind } lets you swap PGlite for a network-attached Postgres or a "sqlite"-family dialect, with working, verified templates for both. mysql/mssql kinds still aren't implemented (Kysely ships dialects for both, but Outer's DDL generation and error mapping don't cover them).