Skip to content
Specific Docs
Esc
navigateopen⌘Jpreview
On this page

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-js dependency.
  • Replace Supabase client calls with fetch requests to the backend service.
  • Send auth tokens in an Authorization: Bearer header 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.

Was this page helpful?