Replacebase is a library to help you migrate away from Supabase over to backend infrastructure that you control. It's a simple Typescript library that wraps your Postgres database and S3 storage, and exposes a Supabase-compatible API. This means you don't have to change any frontend logic and can unify all your backend code for a simpler architecture.
There are many reason to use Replacebase:
- Host your backend where you like, for example on AWS or Specific
- Reduce your dependency on Supabase services and their uptime
- Use a better database and storage provider, like Planetscale or Specific
- Gradually migrate away from using Supabase SDKs to a more flexible backend architecture that you control
- Replace inflexible Supabase services with better alternatives, like Better Auth
Replacebase implements many parts of Supabase (with more to come):
- REST API (can connect to any Postgres database)
- Auth (built on Better Auth)
- Storage (can connect to any S3-compatible service)
- Realtime (broadcast and presence)
- RPC
- Edge functions
- Vectors
This is an early-stage project, use it with care and test thoroughly before using in production
Installation
npm install @specific.dev/replacebase
Getting started
For this guide, we will use your existing Postgres database and Supabase-provided S3-storage and connect Replacebase.
We also offer a skill to let your coding agent help with the migration: npx skills add specific-dev/replacebase
1. Get your Supabase details
Sign in to your Supabase account and retrieve the following:
-
Your Postgres connection string (click "Connect" in the top bar)
-
Your JWT Signing Key URL (go to "Settings" -> "JWT Keys" -> "View key details" -> "Discovery URL")
-
Your legacy JWT secret (go to "Settings" -> "JWT Keys" -> "Legacy JWT Secret")
-
If using storage, your S3 connection details and access key (go to "Storage" -> "S3")
2. Serve up Replacebase
On your backend, initialise Replacebase with the details from step 1.
If you don't have anywhere to host your backend yet, we recommend Specific
// server.ts import { createReplacebase } from "replacebase"; const replacebase = await createReplacebase({ databaseUrl: process.env.DATABASE_URL!, // Supabase Postgres connection string jwksUrl: process.env.JWKS_URL!, // Supabase JWT Signing Key URL jwtSecret: process.env.JWT_SECRET!, // Supabase JWT secret // If using storage, pass Supabase S3 details storage: { s3: { endpoint: process.env.S3_ENDPOINT!, region: process.env.S3_REGION!, accessKeyId: process.env.S3_ACCESS_KEY_ID!, secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!, }, }, });
Next you need to serve up the APIs that Replacebase exposes. Replacebase is framework-agnostic and works with any web server. Pick whichever fits your stack:
Next.js
Since Next.js catch-all routes are mounted under a subpath, use the basePath option so Replacebase matches routes correctly:
// app/api/[...path]/route.ts const replacebase = await createReplacebase({ // ... basePath: "/api", }); export const GET = replacebase.fetch; export const POST = replacebase.fetch; export const PUT = replacebase.fetch; export const PATCH = replacebase.fetch; export const DELETE = replacebase.fetch;
Note: Next.js API routes don't support WebSockets, so Realtime won't work with this setup. If you need Realtime, run Replacebase as a separate backend service.
Express
import express from "express"; const app = express(); app.all("/*", replacebase.toNodeHandler()); const server = app.listen(3000); replacebase.injectWebSocket(server); // Enables Realtime (broadcast + presence)
Hono / Bun / Deno / Cloudflare Workers (standard fetch)
export default { fetch: replacebase.fetch };
For Realtime support on Node.js, use @hono/node-server to get an HTTP server you can inject WebSockets into:
import { serve } from "@hono/node-server"; const server = serve({ fetch: replacebase.fetch, port: 3000 }); replacebase.injectWebSocket(server);
3. Update your client config
The only change you need to make on your frontend is to connect to your own backend with Replacebase instead of Supabase.
import { createClient } from "@supabase/supabase-js"; const supabase = createClient( -"https://xyz.supabase.co", +"http://replacebase-api.spcf.app", // Insert wherever your Replacebase is served from // ... );
That's it, test it out!
Next steps for migration
Replacebase is designed to be a stepping stone to a larger migration away from Supabase. Depending on your goals, you probably want to continue your migration by doing the following:
Change Postgres provider
Since Replacebase connects to any standard Postgres database, you can move off Supabase's hosted Postgres whenever you're ready. Spin up a database on AWS RDS, Specific, or any other provider, migrate your data with pg_dump/pg_restore, and update your DATABASE_URL. Your frontend code will keep working without changes.
Change storage provider
Replacebase works with any S3-compatible storage service. You can switch from Supabase's built-in storage to AWS S3, Cloudflare R2, Specific, or any other provider simply by updating your credentials. Migrate existing files with a tool like rclone or the AWS CLI.
Migrate away from the Supabase SDK
Once Replacebase is running, you can start building regular backend API endpoints alongside it. For example, replace a Supabase client query like supabase.from("posts").select() with a call to your own /api/posts endpoint. With Replacebase, you can do this gradually to move towards a more flexible backend design that better fits your product. Eventually, you can drop @supabase/supabase-js and Replacebase from your stack entirely!
License
MIT