---
title: "Migrate from Supabase"
description: "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](/compare/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.
```

```bash
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:

```bash
supabase db dump --schema public > schema.sql
```

Then declare a [Postgres](/guides/postgres) database in `specific.hcl` and recreate the schema with the migration tool of your chosen stack, or with [Reshape](/guides/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.

```hcl specific.hcl
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](https://www.better-auth.com/) 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](/guides/storage). Declare a `storage` block, point your S3 client at its credentials, and move the objects.

## Realtime

Replace Supabase Realtime with [real-time sync](/guides/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](/guides/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](/guides/crons) 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.
