> ## Documentation Index
> Fetch the complete documentation index at: https://supastack.dev/llms.txt
> Use this file to discover all available pages before exploring further.
> For the full documentation corpus in one file, fetch: https://supastack.dev/llms-full.txt

# Mover um SaaS existente para o supastack

Um manual faseado para migrar um SaaS funcional — auth, tenancy, faturação, jobs — para a fundação supastack sem reescrita nem fim de semana de downtime.

_Publicado 2026-10-29._
_Atualizado em 2026-10-29._

> **Como migro um SaaS existente para o supastack?**
> Depende do teu codebase, mas a sequência não muda: migra por fases, nunca numa só reescrita. Faz o inventário do repositório em código de produto (rotas, schema, lógica de domínio — fica teu) e infraestrutura (auth, tenancy, faturação, jobs, email — o kit substitui-a). Adota os módulos por ordem de dependência: estrutura, auth, workspaces, faturação e depois trabalho em segundo plano. Move os dados com um script que possas correr duas vezes, mantém os IDs de cliente Stripe e faz a transição depois de a aceitação passar. A maioria das apps web demora dias de serões, não semanas.

## Faz o inventário antes de tocar no código

Todas as migrações que correm mal começaram da mesma maneira: alguém abriu o repositório do kit e começou a apagar ficheiros. O kit é uma fundação, não um tema — substitui a tua infraestrutura e não tem opinião sobre o teu produto. Antes de moveres o que quer que seja, divide o teu repositório nas duas pilhas com honestidade.

Código de produto é tudo o que um cliente conseguiria descrever-te de volta: as tuas rotas, o teu schema, a tua lógica de domínio, os teus textos, a tua página de preços. Infraestrutura é tudo o que eles nunca veem: sessões, verificações de tenancy, webhooks de pagamento, jobs em fila, email transacional. A primeira pilha migra intacta. A segunda é substituída — que é exatamente a ideia.

- Lista cada grupo de rotas e etiqueta-o como produto ou canalização — sê implacável; uma página de definições que só alterna um URL de webhook é canalização.
- Lista as tuas variáveis de ambiente e marca quais configuram infraestrutura (segredos de auth, chaves de fornecedores) versus comportamento do produto.
- Lista as tuas tabelas e marca quais ficam a cargo do kit (utilizadores, sessões, workspaces, estado de faturação) versus quais são tuas.
- Escreve a única jornada de utilizador que te paga — esse fluxo é o teste de aceitação de cada fase abaixo.


## Mapeia cada junção a um módulo do kit

A infraestrutura do kit é um conjunto de módulos com contratos entre eles — o teu trabalho é apontar cada peça da tua canalização antiga ao módulo que agora a detém. A maior parte do mapeamento é um-para-um; os sítios onde não é são onde tomas decisões a sério.

Duas escolhas de mapeamento merecem reflexão real em vez de portagem mecânica. Se vens do `Prisma`, o kit padroniza no Drizzle — podes manter o Prisma nas tabelas do produto durante a transição, mas planeia convergir; dois ORMs num só codebase são um imposto de manutenção que se acumula. A [comparação Prisma vs Drizzle](https://supastack.dev/pt/blog/prisma-vs-drizzle-for-saas.md) expõe o que muda.

E se vens de um fornecedor de auth gerida, resiste à vontade de replicar cada funcionalidade do fornecedor antes da mudança. A auth gerida embrulha discretamente coisas que o teu produto pode não usar — listas de sessões, UIs de organizações, sincronização de webhooks. Mapeia o que o teu produto realmente chama; a [comparação de auth](https://supastack.dev/pt/blog/better-auth-vs-nextauth-vs-clerk.md) cobre as diferenças de capacidades que importam.

| A tua canalização atual | Módulo supastack | O que atravessa |
|---|---|---|
| Sessões NextAuth.js / Auth.js | Autenticação (Better Auth) | Utilizadores e contas migram; as sessões são reemitidas na transição |
| Clerk ou um fornecedor de auth gerida | Autenticação (Better Auth) | Os utilizadores migram por email; as sessões de terceiros são abandonadas, não reproduzidas |
| Tabelas próprias de organização/equipa | Espaços de trabalho | As tuas linhas de organização tornam-se linhas de workspace + pertença |
| Webhooks da Stripe feitos à mão | Faturação | Os IDs de cliente Stripe sobrevivem; o estado da subscrição é rederivado |
| Jobs CRON + setTimeout | Tarefas (Trigger.dev) | O trabalho agendado passa para trás do contrato partilhado JobRunner |
| Nodemailer / email ad hoc | E-mail | Os templates migram; o envio passa por um único adaptador de fornecedor |


## Adota os módulos por ordem de dependência

A ordem de migração não é uma preferência — é um grafo de dependências. Cada fase abaixo é lançável separadamente e cada uma produz um sistema funcional que podes verificar antes de a próxima começar. A tentação é fazer tudo num fim de semana e depurar a interseção; resiste-lhe — uma migração faseada significa que cada bug que encontras pertence a exatamente um módulo, um superpoder de depuração que só vais apreciar quando o saltares.

- Fase 1 — esqueleto: faz o scaffold do kit, configura o env, larga as tuas rotas de produto na estrutura do projeto. Ainda nada está ligado; a app arranca e as tuas páginas renderizam.
- Fase 2 — auth: passa o início de sessão para o Better Auth, migra as tabelas de utilizadores/contas, verifica o sign-in + sign-out de ponta a ponta. Todas as fases seguintes dependem de existir uma sessão.
- Fase 3 — tenancy: adota o modelo de workspaces, preenche retroativamente o `workspaceId` nas tuas tabelas de produto, impõe o âmbito nas leituras e escritas. É aqui que o [post sobre fronteiras do workspace](https://supastack.dev/pt/blog/workspace-boundaries.md) se paga a si próprio.
- Fase 4 — faturação: aponta o checkout e os webhooks ao módulo de faturação, remapeia as subscrições existentes, verifica os controlos de direitos contra dados reais da Stripe em modo de teste.
- Fase 5 — trabalho em segundo plano e email: porta os jobs para o contrato JobRunner e passa o email transacional para o adaptador de email. Ficam para o fim porque dependem de utilizadores e workspaces já existentes.


## Move os dados com honestidade

A migração de dados é onde as migrações ganham a sua reputação. As regras que a mantêm aborrecida: escreve um script que possas correr duas vezes, nunca mutes a origem até à transição e verifica contagens em vez de confiar no script.

Utilizadores e contas aterram no schema do Better Auth — o email é a chave de junção, e as contas com palavra-passe exigem uma decisão: ou o teu formato de hash atual é um que o Better Auth consegue verificar, ou lanças um fluxo de reposição de palavra-passe na transição e tratas isso como uma função de força. As contas OAuth de terceiros são o caso fácil — o fornecedor reemite-as no próximo início de sessão.

Na faturação, o caminho honesto é a rederivação em vez de copiar linhas: mantém o ID de cliente Stripe, deixa o tratamento de webhooks do kit reconstruir o estado de direitos a partir da verdade do fornecedor e faz diff do que ele calcula contra os teus registos de subscrição existentes. O [ledger idempotente de webhooks](https://supastack.dev/pt/blog/idempotent-billing-webhooks.md) do kit existe precisamente para que eventos históricos reproduzidos não provisionem nada duas vezes.

```
// migration scripts are boring by design: upsert-shaped,
// re-runnable, and they verify counts instead of trusting themselves
for (const legacyUser of legacyUsers) {
  await upsertAuthUser({
    email: legacyUser.email.toLowerCase(),
    name: legacyUser.displayName,
    externalId: legacyUser.id, // preserved for one-shot joins
  });
}
const migrated = await countAuthUsers();
if (migrated !== legacyUsers.length) {
  throw new Error(`count mismatch: ${migrated} of ${legacyUsers.length}`);
}
```


## O que fica teu

Vale a pena dizê-lo claramente, porque é a parte em que as pessoas erram nas duas direções: as tuas rotas de produto, o teu schema, a tua lógica de domínio e o teu design não mudam. Não estás a reconstruir a tua app dentro do kit — estás a pousá-la em cima dele.

O erro inverso é manter a canalização antiga viva «temporariamente» ao lado do módulo novo. Uma segunda verificação de sessão, um segundo handler de webhooks, uma segunda tabela de direitos — cada sistema paralelo «temporário» é permanente até alguém o apagar, e cada um é um incidente futuro. Substitui, verifica, apaga. Se uma fase ainda não consegue apagar o caminho antigo, não está terminada.


## A checklist da transição

A transição é uma checklist, não um evento. Corre-a num ambiente de staging pelo menos uma vez com dados com formato de produção antes da data real.

- Corre novamente o script de dados contra um snapshot fresco de produção; as contagens e as verificações aleatórias passam.
- Inicia sessão com cada fornecedor de auth que o teu produto suporta — incluindo aqueles cuja existência esqueceste.
- Compra o teu próprio produto no modo de teste da Stripe e segue o webhook até ao direito.
- Envia cada email transacional uma vez; confirma que os links aterram no novo deploy.
- Verifica a jornada pagante de ponta a ponta, depois muda o DNS/env e observa a primeira sessão real.
- Mantém o deploy antigo a correr durante uma semana — o rollback é uma mudança de DNS, não uma migração.


## Perguntas frequentes sobre migrar para um starter kit

O mesmo punhado de perguntas surge sempre que alguém se senta para fazer isto.

- Quanto tempo demora realmente uma migração? — Para uma única app web com auth padrão + Stripe: dias de serões, não semanas. B2B multi-tenant com faturação personalizada acrescenta tempo nas fases três e quatro — é o trabalho de dados que se estica, nunca a mudança de ficheiros.
- Devo migrar ou começar de raiz e portar funcionalidades? — Se o teu código de produto é menor que a tua dívida de infraestrutura, começar de raiz no kit e portar as rotas de produto é muitas vezes mais rápido. Se o teu schema e lógica de domínio são a parte difícil, migra no sítio.
- Posso manter o meu ORM / a minha biblioteca de auth durante a transição? — Por pouco tempo, sim — uma fronteira de módulo de cada vez. Para sempre, não: dois de qualquer coisa na camada de infraestrutura é um imposto que se acumula.
- E os meus webhooks da Stripe já em trânsito? — Aponta o endpoint de webhooks existente ao handler do kit assim que a fase quatro estiver verificada; o ledger idempotente torna eventos reproduzidos ou duplicados seguros em vez de perigosos.
- Tenho de adotar todos os módulos? — Não. Mobile, desktop, jobs e IA são superfícies para adotar ou ignorar — a licença é a fundação, não um mandato para lançar uma app por plataforma.
