SaaS Starter

Supabase

Faça deploy deste boilerplate contra Supabase Postgres + Supabase Storage com o menor diff de config possível.

Supabase é um target de deploy first-class para este boilerplate. Você obtém Postgres + um bucket de storage S3-compatible de um único vendor, e as abstrações existentes do boilerplate de Prisma + storage encaixam sem nenhuma mudança de código — apenas env vars.

Esta página cobre o path recomendado: Supabase como backend de database e file-storage, com BetterAuth rodando em cima de Prisma contra a instância Postgres do Supabase. Não usamos Supabase Auth — BetterAuth já é dono dos flows de auth, sessions, e wiring de OAuth neste codebase.

Fora de escopo: Supabase Realtime e Edge Functions. O boilerplate entrega seu próprio stack Socket.IO + BullMQ para esses concerns.

1. Criar um projeto Supabase

  1. Faça login em supabase.com e crie um novo projeto.
  2. Escolha uma senha forte para o database (você vai precisar no passo 2).
  3. Espere o projeto provisionar (~2 minutos).

2. Pegar as connection strings

Abra Project Settings → Database. Você precisa de duas connection strings:

VariávelSource (Supabase UI)Por quê
DATABASE_URL"Connection pooling" → Transaction modePooled (port 6543); usado pela app rodando para queries curtas
DIRECT_URL"Connection string" → URIDirect (port 5432); usado por prisma migrate para advisory locks

Anexe ?pgbouncer=true&connect_timeout=15 ao DATABASE_URL para que o Prisma saiba que está falando com um pooler.

# .env (server)
DATABASE_URL="postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres?pgbouncer=true&connect_timeout=15"
DIRECT_URL="postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres"

Por que duas URLs? O pooler pgbouncer do Supabase remove advisory locks e sessions long-lived, dos quais migrations Prisma dependem. O runtime usa a URL pooled por eficiência de conexão; migrations usam a URL direta. O block datasource db em prisma/schema.prisma já declara directUrl = env("DIRECT_URL") por essa razão.

3. Rodar migrations

Da raiz do repo:

bunx prisma migrate deploy --schema apps/server/prisma/schema.prisma

O Prisma vai usar DIRECT_URL automaticamente. Verifique que as tabelas chegaram via Table Editor na UI do Supabase.

4. (Opcional) Trocar storage para Supabase

Se você quer que uploads de avatar e quaisquer uploads futuros vão para Supabase Storage em vez de S3 / Cloudflare R2 / disco local:

  1. Criar um bucket. Em Storage → New bucket, nomeie (ex. avatars). Marque "Public" se você quer URLs fetchable pelo browser sem assinar.

  2. Definir uma policy de bucket. Buckets públicos precisam de uma policy SELECT concedendo read access de objetos para anon. Buckets privados não — o adapter cunha URLs assinadas short-lived via presignDownload.

    Exemplo de policy public-read (rode no SQL Editor):

    create policy "public read avatars"
      on storage.objects for select
      to public
      using ( bucket_id = 'avatars' );
  3. Pegue a service-role key. Project Settings → API → Service role. Trate como senha de database — server-only, nunca no browser.

  4. Definir env vars.

    STORAGE_PROVIDER=supabase
    SUPABASE_URL=https://<ref>.supabase.co
    SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
    SUPABASE_STORAGE_BUCKET=avatars
  5. Reinicie o server. O seletor de storage adapter em apps/server/src/modules/storage/infrastructure/providers/index.ts pega STORAGE_PROVIDER=supabase e instancia o adapter Supabase; se alguma var obrigatória estiver faltando, loga um warning e cai para o provider null no-op para que a sequência de boot ainda complete.

5. Verificar

  • GET /healthz deve retornar 200.
  • GET /readyz deve reportar database: ok e storage: ok (o check de storage pinga o bucket via list(limit=1)).
  • Dispare um upload de avatar do client; o arquivo aparece em Storage → <bucket> no Supabase.

O que pulamos deliberadamente

  • Supabase Auth. BetterAuth gerencia sessions, OAuth, e magic-link neste boilerplate. Empilhar Supabase Auth em cima duplicaria essa surface. BetterAuth fala com Supabase Postgres via Prisma como qualquer outro deployment Postgres — sem wiring especial.
  • Realtime. Use o server Socket.IO do boilerplate (já cabeado com adapter opcional Redis para escala horizontal).
  • Edge Functions. Trabalho de background pertence a workers BullMQ (apps/server/src/bootstrap/worker.ts).

Troubleshooting

  • prisma migrate deploy trava ou erra com "advisory lock" — você está apontando migrations ao pooler. Confirme que DIRECT_URL usa port 5432 (não 6543).
  • Upload de avatar retorna 500 com bucket not found — o valor de SUPABASE_STORAGE_BUCKET não bate com um bucket no projeto, ou a service-role key pertence a um projeto diferente.
  • URL pública retorna 403 — bucket é privado e você não adicionou uma policy SELECT. Ou adicione a policy (acima) ou troque o client para fetchar via a URL assinada emitida por presignDownload.

Nesta página