@supabase/lite
v0.7.0
Published
Lightweight TypeScript-native Supabase implementation on SQLite (alpha). PostgREST + GoTrue compatible — use @supabase/supabase-js as-is.
Readme
[!WARNING] 🚧 Alpha. Supalite is pre-1.0 and under active development. APIs, config shape, and on-disk format may change. Not for production use yet. See Feature Overview for current state.

Supabase Lite
Lightweight TypeScript-native Supabase implementation. SQLite as the primary database, with PGlite and Postgres support. Ships a PostgREST-compatible REST API and a GoTrue-compatible Auth API, so @supabase/supabase-js works as-is.
Supalite targets AI builders who want quick, cheap prototypes today with a clear path to upgrade later. It supplements Supabase rather than replacing it. The project stays lightweight by implementing only what fits on top of stock SQLite: roughly 60% of the most-used Supabase features, focused on the subset most useful for fast iteration.
Scope: Both declarative schema (supabase/schemas/*.sql) and imperative Postgres migrations (supabase/migrations/*.sql, Supabase-CLI compatible) are supported. See Migrations. Advanced Postgres-specific column types (ranges, arrays of composites, and similar) are not available.
Validated against the upstream PostgREST and GoTrue spec suites: 1,918 cases passing on SQLite (1,677 Data API + 241 Auth), zero failing. See Testing for per-backend pass rates.
Agents & Skill
If you're an agent working on a supalite project, read these first:
LIMITATIONS.md— what's unsupported / partial, plus anti-patterns. Token-efficient cheat sheet.PATTERNS.md— canonical recipes (per-user RLS, embedded filter workarounds, custom server logic, Vite cold start, triggers).
The npm package ships a supalite skill. After npm install, link it into your agent's skills dir so the cold-start checklist, routing rule, and limitation pointers trigger automatically:
# Agent standard: project-scoped install
mkdir -p .agents/skills && ln -s ../../node_modules/@supabase/lite/skills/supalite .agents/skills/supalite
# Claude Code: project-scoped install
mkdir -p .claude/skills && ln -s ../../node_modules/@supabase/lite/skills/supalite .claude/skills/supaliteThe skill itself points at the installed docs (not their content), so updates land for every consumer on the next npm install.
Feature overview
Compatibility is measured from the @supabase/supabase-js surface. The goal is that code written against Supabase keeps working when pointed at @supabase/lite. Direct database access (raw SQL clients, Postgres wire protocol, psql) is not a target; everything below is scoped to what supabase-js exercises.
For a per-capability parity view with effort estimates and feasibility notes for unsupported features, see FEATURES.md.
| Service | Status | Notes |
|-------------------------|--------|--------------------------------------------------------------------|
| Databases | ✅ | bun:sqlite, node:sqlite, sqlite-wasm, Cloudflare D1 + DO, PGlite, PostgreSQL |
| Data API (PostgREST) | ✅ | 53/74 supabase-js methods on SQLite: from, select, insert, update, delete, upsert, eq, neq, gt, gte, lt, lte, in, notIn, is, isDistinct, like, ilike, likeAllOf, likeAnyOf, ilikeAllOf, ilikeAnyOf, match, or, not, filter, order, limit, range, single, maybeSingle, csv, abortSignal, setHeader, throwOnError, maxAffected, returns, overrideTypes, plus full resource embedding (FK joins, !inner, spreads, nested, aggregates). Partial: contains, containedBy, overlaps, textSearch (LIKE-based lexeme approximation), regexMatch/regexIMatch (simple anchored patterns). rpc not supported on SQLite. 72/74 on Postgres. |
| Auth API (GoTrue) | ✅ | 21/63 supabase-js methods (11 backend + 10 client-side helpers): signUp, signInWithPassword, signInWithOtp, verifyOtp, refreshSession, signOut, getUser, updateUser, resetPasswordForEmail, resend, reauthenticate. OAuth, anonymous, identity linking, admin API, and MFA planned. |
| Storage API | 🧪 | 20/20 supabase-js methods: upload, download, list, remove, move, copy, info, exists, update, getPublicUrl, createSignedUrl, createSignedUrls, createSignedUploadUrl, uploadToSignedUrl, listBuckets, getBucket, createBucket, updateBucket, deleteBucket, emptyBucket. Role-based access + RLS pending. ⚠️ Gated behind EXPERIMENTAL_STORAGE. |
| Realtime | 🔄 | Coming soon |
| Edge Functions | 🔄 | Coming soon |
| Cloud Hosting | 🔄 | Coming soon |
| CLI | ✅ | Upstream supabase CLI parity for: init, start, db diff, db query. Aligned to v2.98.2 command shape. |
| Upgrade to Supabase | 🧪 | lite upgrade migrates a project to hosted or local Supabase: schema + user-table data + auth sessions (signing-key import). Storage/realtime migration pending. SQLite shim health audit via --dry-run. |
Install
npm install -g @supabase/lite # global, exposes `lite` CLI
# or per-project: npm install @supabase/lite (run via `npx @supabase/lite <cmd>`)Quick start
lite init # scaffold supabase/ directory
lite dev # start server with schema hot-reloadThe API is now running at http://localhost:54321. Point @supabase/supabase-js at it:
import { createClient } from "@supabase/supabase-js";
const supabase = createClient("http://localhost:54321", "any-string-works-for-now");
const { data } = await supabase.from("todos").select("*");No anon key is required yet. Pass any non-empty string as the second argument.
Edit supabase/schemas/schema.sql and the dev server re-applies the schema automatically.
When to use what
| Scenario | Use | Notes |
|---|---|---|
| Vite frontend (React/Vue/Svelte/…) | @supabase/lite/vite plugin | Same-process. No separate CLI. API mounted on the Vite dev server. |
| Non-Vite app, want auto schema-reload | lite dev | Separate process. Watches schemas/*.sql, re-applies on change. |
| Non-Vite app, manual control / CI / prod-like | lite start | Separate process. No watch, no auto-migrate. |
Do not run
lite devorlite startalongside the Vite plugin — both bind the API and will collide.
Known limitations across all paths: see LIMITATIONS.md. Canonical recipes: see PATTERNS.md.
CLI
lite <command> [options]By default, command output stays pipe-friendly: SQL, JSON, and query results are
not prefixed with banners or config diagnostics. Use lite --verbose <command>
to show details like the config file and database location on stderr.
Local commands
| Command | Description |
|------------------|---------------------------------------------------------------|
| init | Scaffold supabase/ (config, schema, seed, data dir) |
| dev | Start server + watch schemas/*.sql, auto-apply on change |
| start | Start server (no watch, no auto-migrate) |
| db schema | Print current DB schema; --diff compares vs schemas/*.sql |
| db diff | Emit a new pg-DDL migration from the declarative schema diff |
| db translate | Translate Postgres SQL to this project's backend dialect (arg or stdin) |
| db query | Run a SQL statement against the local DB (arg or stdin) |
| db reset | Drop everything, replay migrations, run seed (--hard = fresh DB file) |
| migration new | Create an empty migration file in supabase/migrations/ |
| migration up | Apply pending migrations (--dry-run to preview) |
| migration list | Show applied vs pending migrations |
| repl | Interactive REPL with app, client, conn in scope |
| upgrade | Migrate the project to hosted or local Supabase (see UPGRADE.md) |
| debug | Show runtime/config info |
Common flags:
lite init --pglite # use PGlite instead of SQLite
lite db reset --hard # delete the DB file, replay migrations + seed
lite db schema --diff # diff current DB vs schemas/*.sql
lite db schema --sql # print raw CREATE statements
lite db diff -f add_col # write a new pg-DDL migration file
lite db query "select count(*) from todos"
# db translate and db query take SQL as an argument or via stdin, so they compose:
cat supabase/migrations/*.sql | lite db translate # inspect the sqlite SQL for a migration
cat supabase/seed.sql | lite db translate | lite db query # translate + apply ad-hoc SQL
lite db translate "alter table public.todos add column done boolean" | lite db query
echo "select * from todos" | lite db query # pipe a one-off statement in
lite upgrade --dry-run # rehearsal plus SQLite shim audit
lite upgrade --dry-run --json # machine-readable shim audit outputUpgrade targets:
lite upgrade --target hosted # default: create/migrate to hosted Supabase
lite upgrade --target local # initialize/migrate local Supabase in the current directory
lite upgrade --target local --local-dir ../my-local-supabaseSee UPGRADE.md for upgrade behavior, target differences, session migration, known gaps, and test strategy.
Telemetry
The lite CLI sends anonymous usage telemetry to help prioritize fixes and features. No personally identifiable information is collected: no file paths, project names, DB URLs, env values, hostnames, IPs, or stack traces. Only the command name, CLI flag presence (boolean, never values), runtime (node/bun/deno), node version, platform/arch, CI/agent/container detection, DB driver (sqlite/sqlite-postgres/pglite/postgres), DB location (file/memory/local/remote), and DB size bucket.
Opt out with any of:
lite --no-telemetry <cmd>
LITE_TELEMETRY=0 lite <cmd>
DO_NOT_TRACK=1 lite <cmd> # https://consoledonottrack.comVite plugin
If your frontend uses Vite, skip the separate CLI + proxy setup. The
@supabase/lite/vite plugin runs the supalite backend inline inside the Vite dev
server, so one process serves both your app and the API.
// vite.config.ts
import { defineConfig } from "vite";
import { supalite } from "@supabase/lite/vite";
export default defineConfig({
plugins: [supalite()],
});Then from your frontend, the canonical snippet works with no setup. The plugin
injects VITE_SUPABASE_URL (the current origin, since the API rides on the Vite
server) and a dev VITE_SUPABASE_ANON_KEY:
import { createClient } from "@supabase/supabase-js";
const client = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY,
);Set either var in a .env file to override the injected defaults — your values
always win.
The plugin auto-resolves ./supabase/config.toml, applies the schema on boot,
watches schemas/*.sql for hot-reload, and mounts /auth/v1, /rest/v1, and
/_system on the Vite server. Active in both vite / vite dev and vite
preview (preview mounts the API and runs boot migrations, but does not watch
schemas — it simulates production).
Do not run
lite devorlite startnext to the plugin — both bind the API and will collide. See When to use what.
See examples/todo for a full Vite + React + Tailwind + RLS example, or examples/next-todo for the same pattern using Next.js App Router catch-all route handlers.
Project layout
lite init creates a Supabase-compatible directory layout:
supabase/
├── config.toml # API port, auth settings, DB path, etc.
├── schemas/
│ └── schema.sql # Postgres DDL, auto-translated to SQLite
├── seed.sql # Seed data, applied after migrations
└── .temp/
└── data.db # SQLite database (git-ignored)config.toml follows the Supabase CLI config format. Minimal example:
[api]
port = 54321
[db]
driver = "sqlite-postgres" # or "sqlite" | "pglite" | "postgres"
url = "file:./supabase/.temp/data.db"
[db.migrations]
schema_paths = ["./schemas/schema.sql"]
[db.seed]
sql_paths = ["./seed.sql"]
[auth]
enabled = true
jwt_secret = "dev-secret-change-me"
jwt_expiry = 3600
enable_signup = true
[auth.email]
enable_confirmations = false
auth.jwt_secret: if omitted, auth falls back to the insecure placeholder"unsafe-secret-change-me"so local dev doesn't break. Always set your own for anything beyond throwaway local use.
Writing schemas
Write Postgres DDL in supabase/schemas/*.sql. When the DB driver is SQLite, DDL is translated on the fly (SERIAL → INTEGER PRIMARY KEY AUTOINCREMENT, NOW() → datetime('now'), JSONB → TEXT with a json_valid() check, and so on). Postgres-only features that don't translate (ranges, LATERAL, table inheritance) throw a descriptive error.
RLS works across all backends: on SQLite, policies are extracted from DDL and enforced at the application layer by rewriting the query AST; on PGlite/Postgres, native RLS is used. auth.uid(), auth.jwt(), and roles (anon, authenticated, service_role) resolve from the JWT.
Full translation reference: STATUS.md#postgres-to-sqlite-translation and app/POSTGRES-SQLITE-COMPAT.md.
Migrations
Imperative Postgres migrations live in supabase/migrations/*.sql (Supabase-CLI compatible — same filename format <14-digit-ts>_<name>.sql, same history table supabase_migrations.schema_migrations). On sqlite-postgres, pglite, and postgres drivers, write raw Postgres DDL — the runtime translates it on the fly. On the bare sqlite driver, write sqlite DDL in supabase/sqlite-migrations/*.sql instead.
lite migration new add_users # create supabase/migrations/<ts>_add_users.sql
# ... edit the file ...
lite migration up # apply pending migrations
lite migration list # show applied vs pending
lite db diff -f tweak # diff schemas/ against applied migrations, emit a new pg-DDL migration
lite db reset # drop everything, replay migrations, run seedMigrations and declarative schemas coexist: lite dev and the Vite plugin apply pending migrations on boot, then run the declarative diff. RLS policies authored in migration files are picked up at request time.
Using @supabase/supabase-js
Two ways to get a client:
Over HTTP
import { createClient } from "@supabase/supabase-js";
const client = createClient("http://localhost:54321", "<anon-key>");
// database
const { data } = await client.from("todos").select("*");
// auth
await client.auth.signUp({ email: "[email protected]", password: "secret123" });
const { data: session } = await client.auth.signInWithPassword({
email: "[email protected]",
password: "secret123",
});In-process (no network)
When you embed the app:
const client = app.getClient();
const { data } = await client.from("todos").select("*");Queries route through app.fetch internally. Same API, no HTTP round trip.
Embedded / programmatic
Embed @supabase/lite in any Web-API-compliant runtime (Bun, Node, browser, and edge runtimes).
Bun / Node
import { App } from "@supabase/lite";
import { createConnection } from "@supabase/lite/sqlite"; // picks driver per runtime
const connection = await createConnection({ url: "file:./data.db" });
const app = new App({ connection, auth: { enabled: true } });
// optionally apply schema on boot
const schema = await Bun.file("./schema.sql").text();
await app.connection.createMigrator(schema).migrate();
export default app; // app.fetch handles requestsSupported databases
| Runtime/DB | Driver |
|--------------|----------------------------------|
| Bun | bun:sqlite (auto) |
| Node.js ≥ 22 | node:sqlite (auto) |
| Browser | @sqlite.org/sqlite-wasm (auto) |
| Workerd | @supabase/lite/workerd |
| PGlite | @supabase/lite/pglite |
| Postgres | @supabase/lite/postgres |
| libsql/Turso | @supabase/lite/libsql |
REPL
lite replGives you an interactive session with app, client (supabase-js), conn, and db in scope. Built-in commands: .tables, .table <name>, .indexes, .config [path].
> await client.from("todos").select("*")
> .tables
> .table todos