Migrate from Supabase
How each Supabase feature maps to Specific: the auto-generated API, row level security, auth, storage, realtime, queues and cron, and what stays the same because it is all Postgres.
Supabase is a backend-as-a-service built around Postgres. It generates a REST API from your schema, and bundles auth, storage, realtime, queues, and cron behind a client SDK. Specific gives you the same Postgres, plus object storage, real-time sync, and durable workflows, but your app talks to a backend service you write, in any language, instead of a generated API.
This page maps each Supabase feature to its Specific equivalent. For a feature-by-feature comparison, see Specific vs Supabase. For the agent-facing version of these instructions, run specific docs /migrations/supabase.
Let your agent do it
The migration is mostly mechanical, which makes it a good job for a coding agent. Install the CLI and give your agent this prompt:
Help me migrate my project from Supabase to Specific. Start with https://docs.specific.dev/for-ai/onboarding, then run `specific docs /migrations/supabase` and follow it.
curl -fsSL https://specific.dev/install.sh | sh
The rest of this page explains what the agent will do, so you can review its work.
Pick a backend stack
Supabase auto-generates a PostgREST API from your database schema. With Specific, an explicit backend service handles routing, request validation, and business logic. Choose the language and framework for that service first: any language works, and the rest of the migration follows from it.
Database
Both Supabase and Specific run Postgres, so the schema transfers directly. Export it from Supabase:
supabase db dump --schema public > schema.sql
Then declare a Postgres database in specific.hcl and recreate the schema with the migration tool of your chosen stack, or with Reshape for zero-downtime migrations. Drop the Supabase-specific parts: RLS policies that call auth.uid(), grants to the authenticated and service_role roles, and references to the auth schema.
build "api" {
base = "node"
command = "npm run build"
}
service "api" {
build = build.api
command = "node dist/index.js"
endpoint {
public = true
}
env = {
PORT = port
DATABASE_URL = postgres.main.url
}
dev {
command = "npm run dev"
}
}
postgres "main" {}
pgvector is available on Specific Postgres. Enable it in a migration with CREATE EXTENSION IF NOT EXISTS vector; and existing embeddings queries carry over unchanged.
Database functions and triggers are standard Postgres and transfer as they are. Functions the frontend called through PostgREST RPC need an explicit endpoint on the backend, which then calls the function over a normal connection.
API and edge functions
Read the frontend code to find every table and RPC it calls through the Supabase client, and implement those as routes on the backend service. Supabase Edge Functions become part of the same service and deploy with it.
Authentication
Supabase Auth has no direct equivalent in Specific; authentication belongs to your stack. Two common choices:
- An authentication library such as Better Auth for TypeScript stacks.
- An external provider such as Auth0, Clerk, or WorkOS.
Store user data in the same Postgres database the service already uses. Supabase Auth keeps its users in an implicit auth schema, so the equivalent tables have to be recreated for the new system.
Authorization and row level security
RLS policies in Supabase rely on Supabase Auth to identify the caller, so they do not translate directly. Read each policy and enforce the same rule in the backend service, where the authenticated user is known.
Storage
Supabase Storage is S3-compatible, and so is Specific object storage. Declare a storage block, point your S3 client at its credentials, and move the objects.
Realtime
Replace Supabase Realtime with real-time sync, which streams Postgres changes to clients over HTTP. Check first whether polling is enough for the feature in question.
Queues and cron
Replace Supabase Queues with a Temporal workflow engine: declare a temporal block and run a worker service that processes queued work as workflows. Replace pg_cron with either a cron block for simple scheduled commands or Temporal Schedules for multi-step scheduled work.
Client-side changes
- Remove the
@supabase/supabase-jsdependency. - Replace Supabase client calls with
fetchrequests to the backend service. - Send auth tokens in an
Authorization: Bearerheader on authenticated requests.
Secrets
Declare the secrets and configs the app still needs in specific.hcl and put their values in specific.local so specific dev does not prompt for them. The Supabase variables (SUPABASE_URL, SUPABASE_ANON_KEY, and the service role key) are no longer needed.
Data migration
Moving rows out of Supabase Postgres and objects out of Supabase Storage is currently a manual step: dump and restore the data with standard Postgres and S3 tooling.