Pular para o conteúdo
🔄 GUIAS

Migrar do Supabase em 1 linha.

O SuperDB é drop-in compatible com @supabase/supabase-js. Mude o import, suba os dados, pronto. Este guia cobre SDK, schema, usuários, storage e realtime — com tempo estimado para cada etapa.

Quanto tempo leva?

Depende do tamanho do projeto, mas dá pra estimar bem. A maioria das migrações pequenas (até ~10k usuários, < 5GB) cabe num turno de trabalho. Schema e SDK são as partes rápidas; usuários e storage variam com o volume.

EtapaTempo típicoDepende de
Troca de SDK (1 linha de import)~5 minTamanho do codebase pra rebuild + smoke test
Schema (export + import)~30 minQuantidade de tabelas, extensions e funções
Migração de usuários~1h pra < 10kVolume; senhas em hash continuam funcionando
Storage (cópia de arquivos)VariaVolume total em GB e largura de banda
Total típico (projeto pequeno)2-4hInclui smoke test e ajustes finos
💡

Dica: rode os 2 sistemas em paralelo durante a migração. Como o SDK é compatível, dá pra apontar o staging pro SuperDB e o prod pro Supabase, validar tudo, e só então virar o DNS / env vars.

Pré-requisitos

  • Node 18 ou superior
  • Acesso ao projeto Supabase com permissão pra export (owner ou admin)
  • psql instalado localmente (vem com o Postgres client)
  • Conta SuperDB criada em app.superdb.com.br
  • Projeto SuperDB provisionado (anote URL, anon key e service-role key)

Passo 1: troque o SDK

O @superdb/supabase-compat implementa a mesma API pública do supabase-js v2. Não muda nenhuma chamada no seu código — só o pacote de origem.

Terminal
npm uninstall @supabase/supabase-js
npm install @superdb/supabase-compat

Depois mude o import. Esse é o diff que normalmente é o único do projeto:

lib/db.ts — diff
- import { createClient } from '@supabase/supabase-js'
+ import { createClient } from '@superdb/supabase-compat'

  export const db = createClient(
-   process.env.NEXT_PUBLIC_SUPABASE_URL!,
-   process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
+   process.env.NEXT_PUBLIC_SUPERDB_URL!,
+   process.env.NEXT_PUBLIC_SUPERDB_ANON_KEY!
  )

Pronto. Todos os métodos (.from().select(), .auth.signUp(), .storage.from(), .channel()) continuam funcionando exatamente igual.

ℹ️

Nota: se você usa supabase-js em projetos serverless (Edge Functions, Vercel Functions, Cloudflare Workers), o @superdb/supabase-compat também roda nesses runtimes. Mesmo bundle, mesma dependência de fetch nativo.

Passo 2: exporte o schema

O Supabase é Postgres, então um pg_dump resolve. Você consegue a connection string em Project Settings → Database → Connection string (use a do tipo "Session pooler" pra evitar limite de conexões).

Terminal — export do Supabase
# 1. Dump completo (schema + dados) excluindo schemas internos do Supabase
pg_dump \
  "postgresql://postgres.[REF]:[PASS]@aws-0-[REGION].pooler.supabase.com:5432/postgres" \
  --no-owner \
  --no-privileges \
  --schema=public \
  --schema=storage \
  --exclude-table=storage.migrations \
  --exclude-table=storage.objects \
  -f supabase-dump.sql

# 2. Só o schema (sem dados) — útil pra inspecionar antes de importar
pg_dump \
  "postgresql://postgres.[REF]:[PASS]@aws-0-[REGION].pooler.supabase.com:5432/postgres" \
  --schema-only \
  --schema=public \
  --no-owner \
  -f schema.sql

Agora importe no SuperDB. A connection string do seu projeto está em Dashboard → Conexão direta.

Terminal — import no SuperDB
psql "postgresql://postgres.[PROJ_ID]:[PASS]@db.superdb.com.br:5432/postgres" \
  -f supabase-dump.sql

Ou, se preferir interface visual, abra o Studio do SuperDB e cole o SQL no editor.

app.superdb.com.br/dash/meu-app/editor
v0.4.2
import.sql ×
1-- importado de supabase-dump.sql
2create table public.posts (
3 id uuid primary key default gen_random_uuid(),
4 user_id uuid references auth.users(id),
5 title text not null,
6 created_at timestamptz default now()
7);
⚠️

Cuidado com extensions e schemas custom: se seu projeto usa pgvector, pg_cron, pgsodium ou similares, confirme que estão habilitadas no SuperDB antes de rodar o import (Dashboard → Banco → Extensions). Schemas custom (fora de public) também precisam ser criados manualmente antes do psql -f.

Passo 3: migre os usuários

Os 2 sistemas guardam usuários numa tabela auth.users com schema muito parecido. A diferença prática: no SuperDB, cada projeto tem seu próprio schema proj_X com a tabela users. As senhas em hash (bcrypt ou Argon2id) continuam funcionando sem reset.

Você tem 2 opções: (a) via SQL direto (CSV intermediário) ou (b) via Foreign Data Wrapper se ainda tem acesso ativo ao Supabase.

Opção A — export pra CSV e import

Terminal — export users do Supabase
psql "postgresql://postgres.[REF]:[PASS]@aws-0-[REGION].pooler.supabase.com:5432/postgres" \
  -c "\COPY (SELECT id, email, encrypted_password, email_confirmed_at, created_at, raw_user_meta_data FROM auth.users) TO 'users.csv' WITH CSV HEADER"
SQL — import no SuperDB
-- substitua proj_X pelo schema do seu projeto SuperDB
CREATE TEMP TABLE _import_users (
  id uuid,
  email text,
  encrypted_password text,
  email_confirmed_at timestamptz,
  created_at timestamptz,
  raw_user_meta_data jsonb
);

\COPY _import_users FROM 'users.csv' WITH CSV HEADER;

INSERT INTO proj_X.users (id, email, encrypted_password, email_confirmed_at, created_at, raw_user_meta_data)
SELECT id, email, encrypted_password, email_confirmed_at, created_at, raw_user_meta_data
FROM _import_users
ON CONFLICT (email) DO NOTHING;
💡

Senhas: o Supabase usa bcrypt; o SuperDB aceita bcrypt e re-hash lazy pra Argon2id no próximo login bem-sucedido. Ou seja: nenhum usuário precisa resetar senha. O hash original migra junto.

Passo 4: migre o storage

Storage é cópia de arquivos + metadata. O script abaixo lista todos os objects do bucket Supabase, baixa um por um e re-upload no SuperDB mantendo o path. Roda em background, sem bloquear o resto.

scripts/migrate-storage.ts
import { createClient as createSupa } from '@supabase/supabase-js'
import { createClient as createSuper } from '@superdb/supabase-compat'

const supa = createSupa(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE!
)
const sup = createSuper(
  process.env.SUPERDB_URL!,
  process.env.SUPERDB_SERVICE_ROLE!
)

const BUCKET = 'avatars'

async function migrate() {
  const { data: files, error } = await supa.storage
    .from(BUCKET)
    .list('', { limit: 10_000, sortBy: { column: 'name', order: 'asc' } })

  if (error) throw error

  for (const f of files ?? []) {
    const { data: blob } = await supa.storage.from(BUCKET).download(f.name)
    if (!blob) continue

    const { error: upErr } = await sup.storage
      .from(BUCKET)
      .upload(f.name, blob, { upsert: true, contentType: f.metadata?.mimetype })

    if (upErr) console.error(`[fail] ${f.name}:`, upErr.message)
    else console.log(`[ok]   ${f.name} (${f.metadata?.size} bytes)`)
  }
}

migrate().catch(console.error)

Rode com npx tsx scripts/migrate-storage.ts. Pra buckets grandes, paginate com offset e considere rodar em paralelo (Promise.all com batches de 10).

Passo 5: realtime

Aqui é onde a compatibilidade brilha: nenhuma mudança de código. A API .channel().on('postgres_changes', ...) funciona idêntica. Os triggers e a publicação supabase_realtime vêm no dump SQL e ficam ativos no SuperDB sem ajustes.

app/chat.tsx — sem alterações
const channel = db
  .channel('room-1')
  .on('postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'messages' },
    (payload) => setMessages((m) => [...m, payload.new])
  )
  .subscribe()
💡

Performance: o realtime do SuperDB usa o mesmo protocolo do Supabase Realtime (WebSocket + Phoenix Channels). Latência típica em São Paulo: ~30ms. Confirme que a tabela está na publicação rodando SELECT * FROM pg_publication_tables WHERE pubname = 'supabase_realtime';

Passo 6: revise RLS

As políticas RLS do Supabase são SQL puro — copiam direto. A sintaxe é a mesma e as funções auth.uid(), auth.jwt(), auth.role() continuam disponíveis com o mesmo comportamento.

SQL — política exemplo (funciona sem mudança)
create policy "users can read their own posts"
  on public.posts
  for select
  using (auth.uid() = user_id);

create policy "users can insert their own posts"
  on public.posts
  for insert
  with check (auth.uid() = user_id);
⚠️

Atenção: as políticas vêm no pg_dump, mas sempre rode um \d+ public.tabela no SuperDB depois pra confirmar. Se o seu app usa auth.uid() dentro de funções (não só em políticas), valide que essas funções foram criadas com o search_path correto.

Passo 7: troque as env vars

Mude o .env e os secrets em Vercel / Netlify / Railway. As chaves do SuperDB ficam em Dashboard → API Keys.

.env — diff
- NEXT_PUBLIC_SUPABASE_URL=https://abcdefgh.supabase.co
- NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...
- SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...
+ NEXT_PUBLIC_SUPERDB_URL=https://api.superdb.com.br
+ NEXT_PUBLIC_SUPERDB_ANON_KEY=sdp_anon_...
+ SUPERDB_SERVICE_ROLE_KEY=sdp_service_...

Se você prefere não renomear as variáveis, o @superdb/supabase-compat também lê NEXT_PUBLIC_SUPABASE_URL e NEXT_PUBLIC_SUPABASE_ANON_KEY se eles apontarem pro SuperDB. Mas renomear é mais limpo e evita confusão futura.

Passo 8: deploy

  1. Trigger um rebuild no Vercel / Netlify (qualquer alteração de env var força rebuild).
  2. Smoke test num preview deployment: login, signup, leitura, escrita, upload, realtime.
  3. Confirme que a página de login não está com cache do CDN servindo o bundle antigo.
  4. Monitore o Dashboard SuperDB nas primeiras horas: queries lentas, taxa de erro, throughput.
  5. Mantenha o projeto Supabase em standby por ~48h em caso de rollback.

Checklist final

Antes de considerar a migração completa:

  • ☐ SDK trocado (@superdb/supabase-compat instalado e import atualizado)
  • ☐ Schema importado e validado (todas as tabelas, views, functions, triggers)
  • ☐ Usuários migrados (login com senha antiga funciona)
  • ☐ Storage copiado (cheque um arquivo aleatório baixando pela URL)
  • ☐ RLS testado (policy bloqueia user A de ler dado do user B)
  • ☐ Env vars trocadas em prod, staging e local
  • ☐ OAuth providers (Google / GitHub / Apple) reconfigurados com novas callback URLs
  • ☐ MFA TOTP preservada (usuários com MFA ativa conseguem entrar)
  • ☐ Magic links testados de ponta a ponta
  • ☐ Smoke test passou em todos os fluxos críticos

E se eu quiser voltar?

Como nada foi destruído no Supabase original, rollback é só:

  1. Reverter as env vars pro Supabase (renomeia ou troca os valores).
  2. Reverter o import do SDK (npm install @supabase/supabase-js).
  3. Redeploy.

Os 2 sistemas convivem em paralelo durante a migração. Dados criados no SuperDB depois do switchover precisariam ser re-sincronizados de volta — por isso recomendamos manter o paralelismo curto (24-48h) e validar bem antes.

ℹ️

Precisa de ajuda? Migrações de projetos médios e grandes podem ter pontas soltas (custom triggers, RLS complexas, integrações com webhooks). Mande um email pra contato@superdb.com.br — a gente acompanha a migração junto, sem custo.

Essa página ajudou?