backlex
Open-source (Apache-2.0) AI-native backend you can self-host or run at the edge. One codebase on Bun / Node / Deno that also deploys to Cloudflare Workers, Vercel, Netlify, Deno Deploy, AWS Lambda, Google Cloud and Azure — on PostgreSQL or SQLite / D1.
An open-source alternative to Supabase, Firebase, Directus, Appwrite, Strapi and AWS Amplify: every feature lives in the Apache-2.0 core — no Enterprise tier for SSO, audit logs, or permissions. Agents reach your data through a built-in MCP server, scoped by the same permissions DSL as everything else.
Dynamic schema · Adopt existing tables · Permissions DSL · REST + GraphQL
Realtime (SSE) · Reactive live queries · Offline-first sync · Full-text + vector search
AI agents · Built-in MCP server · Edge functions (sandbox) · Flows · Durable jobs · Cron
Storage + folders + image transforms · Resumable uploads · Draft/publish · Feature flags
Push + SMS messaging · Webhooks · Embedded BI dashboards · Audit logs · Tracing · Backup/restore
Multi-tenant workspaces · SSO (SAML/LDAP) · Passkey · Advisor · Admin UI · Typed SDK · CLI · Type gen
Stack
| Layer | Tech |
|---|---|
| API | Hono — one app on Bun / Node / Deno + Workers, Vercel, Netlify, Lambda, GCP, Azure |
| ORM | Drizzle v1 beta — PG + SQLite/D1 |
| Auth | better-auth — email, OAuth (Google/GitHub/Apple), magic-link, OTP, passkey, SAML 2.0 SSO, LDAP/AD |
| Storage | local FS (Bun dev) / Cloudflare R2 (Workers) / S3-compatible (any runtime: AWS, R2, B2, MinIO, Spaces, Wasabi) |
| Vectors | pgvector (PG) / Cloudflare Vectorize (Workers) |
| Realtime | SSE in Bun / Durable Objects on Workers |
| Sandbox | Bun worker thread / QuickJS-WASM / remote HTTP executor |
| Image | Bun.Image (Bun) / Cloudflare Image Resizing (Workers) / passthrough |
| GraphQL | graphql-yoga, schema auto-generated from collections |
| Admin UI | Vite + React + shadcn/ui + Tailwind v4 |
| Monorepo | Bun workspaces |
Layout
apps/
web/ One app — Hono API + Vite + React admin SPA in a single bundle
(server/ + client/ + entries/{bun,worker,vercel,netlify}.ts)
packages/
core/ Shared types + adapter interfaces
db/ Drizzle schemas + dynamic-schema applier + permission compiler
auth/ better-auth wrapper (email + OAuth + plugins + passkey)
ui/ shadcn radix-luma component library
client/ Typed SDK (browser + Node)
cli/ `backlex` CLI (migrate, gen-types)
Quick start
Prereqs: Bun ≥ 1.1.
bun install
cp apps/web/.dev.vars.example apps/web/.dev.vars
# Apply migrations to local SQLite (default for dev)
bun run db:migrate:sqlite
# Start Vite + Cloudflare miniflare in one process on :5173
# (admin SPA + Worker bundled — no separate API port, no proxy)
bun run dev
Sign up the first user at http://localhost:5173/sign-up — they automatically
get the admin role. Subsequent sign-ups get authenticated.
DB selection (auto)
The API picks a database based on bindings/env in this order:
D1binding (Cloudflare Workers) → D1 SQLiteDATABASE_URL→ Postgres viapostgres-js- otherwise → Bun SQLite at
./.data/backlex.sqlite
Deploy targets
| Target | Database | Storage | Realtime | Sandbox |
|---|---|---|---|---|
| Bun (self-host) | SQLite or Postgres | local fs / S3 (Bun.S3Client) |
in-proc + SSE | Worker thread |
| Cloudflare Workers | D1 or Hyperdrive→PG | R2 / S3 (aws4fetch) |
Durable Objects | QuickJS / remote HTTP |
| Vercel Edge | Postgres (Neon HTTP) | S3 (aws4fetch) |
SSE | QuickJS |
| Netlify Edge | Postgres (Neon HTTP) | S3 (aws4fetch) |
SSE | QuickJS |
Set S3_BUCKET + S3_ACCESS_KEY_ID + S3_SECRET_ACCESS_KEY (and
optionally S3_ENDPOINT for non-AWS) and the storage adapter picks the
S3 path automatically. See Storage on edge.
Bun (self-host)
APP_URL=https://your.app DATABASE_URL=postgres://... \
AUTH_SECRET=$(openssl rand -hex 32) \
bun run --cwd apps/web dev:bun
Cloudflare Workers
cd apps/web
wrangler d1 create backlex # paste id into wrangler.toml
wrangler r2 bucket create backlex-files
wrangler vectorize create backlex-embeddings --dimensions=1536 --metric=cosine
wrangler secret put AUTH_SECRET
wrangler d1 migrations apply backlex --remote
wrangler deploy
Optional: run the out-of-isolate function executor (templates/fn-exec-server)
on Fly / Railway / a VM for DB-aware functions, then set FUNCTIONS_EXEC_URL
SANDBOX_RPC_TOKEN+SELF_URLon the Worker so theremote-httpsandbox provider routes there. Without it, functions run in the in-isolate QuickJS-WASM sandbox (sync only, noctx.*host I/O).
Vercel
vercel.json at the repo root deploys both admin (static) and API (edge).
vercel link
vercel env add DATABASE_URL # Postgres URL (Neon recommended for edge)
vercel env add AUTH_SECRET
vercel deploy --prod
Cron triggers (* * * * * in vercel.json) hit /api/_cron/tick and call
the same cronTick the Bun scheduler uses.
Netlify
netlify.toml at the repo root mirrors the Vercel layout — admin SPA +
edge function for /api/* + scheduled function for cron.
netlify init
netlify env:set DATABASE_URL postgres://...
netlify env:set AUTH_SECRET $(openssl rand -hex 32)
netlify deploy --prod
API surface
GET /health
* /api/auth/** better-auth (email, OAuth, magic-link, OTP, passkey)
GET /api/api-keys list
POST /api/api-keys create — secret returned once
DELETE /api/api-keys/:id revoke
GET /api/collections list (active by default; ?include_archived=true)
POST /api/collections unified: managed (CREATE TABLE c_<prefix>_<slug>) or adopted (metadata only)
PATCH /api/collections/:slug additive ALTER TABLE; no-op on adopted
DELETE /api/collections/:slug DROP TABLE on managed; soft-archive on adopted
POST /api/collections/:slug/restore un-archive an adopted collection
GET /api/admin/adopt/tables list tables eligible for adoption
POST /api/admin/adopt/inspect inspect columns + FKs of a candidate table
GET /api/items/:slug filter / sort / fields / expand / q / locale / limit / offset / meta
GET /api/items/:slug/:id (also accepts ?expand=)
POST /api/items/:slug
PATCH /api/items/:slug/:id
DELETE /api/items/:slug/:id
GET /api/storage list (permission-filtered)
PUT /api/storage/:key upload (raw body, ?folderId=)
GET /api/storage/:key download (?width &height &format &fit &quality for image transforms)
DELETE /api/storage/:key
GET /api/folders list
POST /api/folders create
GET /api/activity list activity entries
GET /api/revisions/:collection/:itemId
POST /api/revisions/:id/revert
GET /api/realtime/items:<slug>/subscribe permission-filtered change feed (SSE, with Last-Event-ID resume)
GET /api/realtime/collections/subscribe admin-only schema events (SSE)
GET /api/realtime/presence:<name>/subscribe signed-in members roster (SSE)
* /api/realtime/:channel/{subscribe,publish} free-form (no filter; publish rate-limited)
POST /api/realtime/items:<slug>/test-publish admin-only synthetic event injector
GET /api/webhooks admin
POST /api/webhooks admin
GET /api/flows admin
POST /api/flows admin
GET /api/functions admin
POST /api/functions admin
POST /api/functions/:name/invoke
GET /api/roles admin
POST /api/roles admin
GET /api/permissions
DELETE /api/permissions/:id admin
GET /api/users admin
POST /api/users/:id/roles admin
GET /api/account/preferences per-user locale/timezone (resolved + raw)
PATCH /api/account/preferences update per-user locale/timezone
GET /api/admin/saml/providers admin — list per-tenant SAML providers
GET /api/admin/ldap-config admin — per-tenant LDAP config (secrets write-only)
GET /api/admin/email-config admin — per-workspace email transport
GET /api/admin/advisor admin — schema + permissions lint findings + score
GET /api/admin/settings admin — workspace settings (whitelist PATCH)
POST /api/t/<slug>/auth/saml/<provider>/{login,acs,metadata,slo} per-tenant SAML
POST /api/t/<slug>/auth/ldap/sign-in per-tenant LDAP sign-in
POST /api/t/<slug>/auth/token/refresh refresh-token → access-token JWT
GET /api/i18n workspace content translations
GET /api/notifications per-user notification feed
GET /api/comments per-item comment threads
GET /api/metrics admin — overview KPIs / charts
GET /api/activity audit log (admin sees all; others own rows)
* /api/graphql GraphQL (queries + mutations)
GET /api/openapi OpenAPI 3.1 description of the public surface
GET /api/_cron/tick internal — used by Vercel/Netlify cron
POST /api/_internal/sandbox-rpc internal — Bearer-auth, used by the remote-http executor
Documentation
- Getting started — first user, first collection, first item
- Deployment — self-host (Bun / Node / Deno) or deploy to Cloudflare, Vercel, Netlify, Deno Deploy, AWS Lambda, Google Cloud & Azure
- Permissions DSL — operators, variables, examples
- Querying items — filter / sort / projection / expand / locale / meta
- Adopting tables — wrap an existing table without DDL
- Functions / sandbox — three providers, RPC bridge, security
- SDK + CLI — the
backlexTypeScript SDK + thebacklexCLI - MCP server — expose a workspace to Claude / Cursor / agents, scoped per key
- AI agents — reason→act loop, DSL-scoped tools, per-thread vector memory
- Vector + full-text search — pgvector / Vectorize, auto-embed on write
- GraphQL — auto-schema, relations, mutations
- Realtime — channels, permission filtering, hosting
- Storage — adapters, image transforms, signed URLs
- SSO + LDAP — per-tenant SAML 2.0, LDAP / Active Directory
- Advisor — automated lint over schema, permissions, config
- Locale + timezone — workspace + per-user preferences
- Admin SPA translation — Lingui catalogs for the admin chrome
- Adapter pattern — runtime-agnostic interfaces
- Design system — admin tokens, layout principles, component contracts, voice
Adapter pattern
Cross-runtime concerns live behind interfaces in @backlex/core/adapters:
StorageAdapter—fsStorage(Bun dev) /r2Storage(Workers) /bunS3Storage(Bun + S3) /s3FetchStorage(any runtime + S3)VectorAdapter—pgvectorAdapter(PG) /vectorizeAdapter(Workers)RealtimeAdapter— in-proc + SSE (Bun) / Durable Object + WS (Workers)EmailAdapter—consoleEmail(dev) /resendEmail/sendgridEmail/mailgunEmail/sesEmail(HTTP APIs, any runtime) /smtpEmail(nodemailer, not on Workers) — pick viaEMAIL_PROVIDER, or use per-workspaceemail_configSamlAdapter—samlify(works on all runtimes vianodejs_compaton Workers); per-tenant configs insaml_providersLdapAdapter—ldapts(Bun / Vercel / Netlify); Workers fall through to a throwing shim — use SAML thereImageAdapter—bunImage(Bun.Image) /cfImage(CF Image Resize) /passthroughImage
apps/web/src/server/context.ts picks the right adapter based on bindings/env.
License
Apache-2.0