Supabase-compatible backend in a single binary — real Postgres with RLS, no Docker. Works with supabase-js unchanged.
翻译准备中
正在加载完整 README。
查看完整 README ↗Local Supabase dev without Docker — one process, real Postgres, and it even runs in the browser. It speaks the same wire protocols as hosted Supabase, so the official @supabase/supabase-js SDK works unchanged - REST, Auth, Storage, and Realtime.
A pure-JS backend built on PGlite (Postgres compiled to WASM), with an embedded-native-Postgres and an ultralight pure-JS (@tinbase/pg-mem) engine too. One command, real Row Level Security, and 1:1 with Supabase's APIs and migration conventions.
[!WARNING] Alpha — not production-ready yet. Great for local development, prototypes, and embedded/browser use.
npx tinbase start
@electric-sql/pglite.auth.uid(), triggers, FKs - it is Postgres.--engine wasm runs PGlite (Postgres compiled to WASM) instead - portable, browser-ready, and the default on Windows. --engine pgmem is an ultralight pure-JS in-memory engine for local dev / previews - see Engines.supabase/migrations/*.sql and supabase/seed.sql; tracks them in supabase_migrations.schema_migrations. Your migration files stay portable to hosted Supabase.CREATE EXTENSION statements for extensions tinbase can't install (pg_cron, pg_net, http, hypopg, …) are skipped rather than aborting; CREATE INDEX CONCURRENTLY is applied without the (transaction-illegal) CONCURRENTLY; each migration runs with a fresh search_path like the Supabase CLI; and Vault, pgmq, cron, pg_net, and moddatetime are emulated. As a test, Cap-go/capgo's 335 migrations + seed apply and query cleanly.(Request) => Response fetch handler. In Node it's served over HTTP; in the browser you can hand it to supabase-js as a custom fetch and run the whole backend in-process (PGlite already runs in the browser via IndexedDB/OPFS).# in a project with a supabase/ directory (or none - it still boots)
npx tinbase start
# API URL: http://127.0.0.1:54321
# anon key: eyJ...
# service_role key: eyJ...
The keys are the same on every start and on every machine (they're the
well-known Supabase local-dev demo keys), so you can commit them straight into
a shared .env.local - same as supabase start:
# .env.local - safe to commit, identical for every teammate
SUPABASE_URL=http://127.0.0.1:54321
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
With a custom --jwt-secret the keys are still deterministic (stable per
secret); under NODE_ENV=production every start signs fresh, unique keys.
Point the ordinary supabase-js client at it:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('http://127.0.0.1:54321', ANON_KEY)
await supabase.auth.signUp({ email: '[email protected]', password: 'secret123' })
await supabase.from('todos').insert({ title: 'hello' })
const { data } = await supabase.from('todos').select('*, author:users(name)').eq('done', false)
supabase
.channel('feed')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'todos' }, console.log)
.subscribe()
tinbase start # boot the server (applies pending migrations first)
tinbase migrate # apply pending migrations and exit
tinbase status # list applied migrations
tinbase keys # print anon / service_role keys (deterministic in dev, unique when NODE_ENV=production)
tinbase keys --s3 # print the S3 access key id, secret and region
tinbase gen types # print a TypeScript Database type for the schema
tinbase db reset # wipe the database + storage, re-run migrations and seed
tinbase db diff # DDL for schema changes not yet in migrations (-f <name> to save one)
tinbase db export # database, roles and stored files to a small archive (-o <file>; native engine, works while serving)
tinbase db import # create the database from such an archive (<file>; --force replaces a stopped one)
tinbase db pristine # does the database still hold only what migrations and seed put there? (exit 0 yes, 3 no)
# start restores an export left at <dir>/.tinbase/restore.tar.gz before opening the database
-p, --port <n> port (default 54321; or TINBASE_PORT / PORT env)
--dir <path> project dir containing supabase/ (default cwd)
--data-dir <path> PGlite data dir (default <dir>/.tinbase/db)
--jwt-secret <s> JWT secret (or TINBASE_JWT_SECRET)
--memory in-memory database, no persistence (wasm engine)
--engine <e> native (default), wasm, or pgmem
~/.cache/tinbase), then initdb with memory-lean settings. ~59 MB RAM at boot. Listens only on a private unix socket (0700 dir, trust auth) - never TCP. macOS/Linux on x64/arm64.以上为 README 的开头部分。其余内容请阅读原文。 查看完整 README ↗