Migrating from Fakebase to real Supabase
Fakebase is built to be thrown away. The point of the local runtime is to get you to a working prototype fast; the point of the export path is to make the switch to a real Supabase project boring and low-risk. This guide walks the full handoff.
TL;DR
fakebase migrate export --supabase # supabase/migrations/*.sql
fakebase seed export # supabase/seed.sql
fakebase types gen # database.types.ts
fakebase verify supabase # compatibility suite vs a local Supabase stack
Then swap the client import and environment variables. That's it.
1. Stabilize your schema
Author your schema either in the TypeScript DSL (fakebase/schema.ts) or as SQL migrations.
Both normalize to the same schema IR. When the shape stops changing, you're ready to export.
fakebase migrate diff # preview the SQL diff against the current IR
fakebase migrate new add_profiles # create a named, timestamped migration
2. Export Supabase-compatible artifacts
fakebase migrate export --supabase
This writes conventional Supabase files:
supabase/
migrations/
20260601120000_init.sql
20260601130000_add_profiles.sql
seed.sql # via `fakebase seed export`
database.types.ts # via `fakebase types gen`
The generated SQL uses standard PostgreSQL DDL (create table if not exists, alter table ... enable row level security, policies, indexes) so it applies cleanly with the Supabase CLI or the
dashboard SQL editor.
Review the generated SQL. Fakebase emits faithful DDL, but you own the production schema — confirm types, defaults, constraints, and especially RLS policies before applying.
3. Generate types
fakebase types gen
The generated database.types.ts has the same shape Supabase's generator produces — a Database
interface with Tables, Views, Functions, Enums, and CompositeTypes, plus per-table
Row / Insert / Update and the Tables<>, TablesInsert<>, TablesUpdate<> helpers. Your
app's typed queries keep working after the swap.
4. Verify against real Supabase
This is the non-negotiable step. Local RLS, auth, and SQL semantics are approximations.
# with a local Supabase stack running (supabase start)
fakebase verify supabase
verify runs the supported-scenario suite against both Fakebase and the real stack and prints a
matrix of exact match / acceptable approximation / unsupported. Resolve anything that doesn't
match — most commonly RLS policies and rpc functions that relied on JS evaluation locally.
5. Swap the client
The call shape is the same, so the diff is small:
- import { createClient, createMemoryKernel } from "@byronwade/fakebase";
- const kernel = createMemoryKernel<Database>({ schema });
- export const supabase = createClient<Database>("http://localhost", "local", { kernel });
+ import { createClient } from "@supabase/supabase-js";
+ export const supabase = createClient<Database>(
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
+ );
For Next.js SSR, switch to @supabase/ssr cookie helpers. Because Fakebase already used
cookie-based sessions and PKCE-shaped exchange on the server, your route handlers and Server
Components keep the same structure.
6. Production handoff checklist
fakebase ai init generates a migration.checklist.md, but the essentials are:
-
supabase/migrations/*.sqlapplied to the real project -
supabase/seed.sqlreviewed (don't seed production with dev data by accident) -
database.types.tsregenerated from the real project -
@byronwade/*dependencies removed from the app's runtime path -
.fakebase/excluded from production builds - Auth flows tested against real Supabase Auth
- RLS policies tested against real PostgreSQL (not Fakebase's JS approximation)
- Storage buckets and access rules recreated
- Realtime subscriptions tested
What will not transfer automatically
- RLS correctness. Fakebase evaluates policies in JavaScript. Real enforcement happens in
PostgreSQL with different planner and
SECURITY DEFINERsemantics. rpcbodies. Locally registered JS functions must be rewritten as SQL/PL-pgSQL functions.- Edge Functions. Local handlers run as plain JS; port them to Supabase Edge Functions (Deno).
- Signed URLs. Local tokens are deterministic placeholders, not CDN-signed URLs.
See security.md for why these boundaries exist.