Adios
BlogNext.js SaaS

Next.js SaaS

Como criar um SaaS com Next.js para produção: autenticação, cobrança, tarefas e implantação

Crie um SaaS Next.js 16 pronto para produção, com autenticação, Postgres, assinaturas, tarefas em segundo plano, configuração segura, testes e implantação.

Equipe AdiosAtualizado 17 de julho de 202610 minutos de leitura

Um SaaS fica complexo quando as funcionalidades dependem de estado: identidade, autorização, cobrança, novas tentativas, migrações e versões. A árvore de componentes React é apenas uma parte do sistema.

Definir o primeiro fluxo completo do cliente

Comece com um fluxo que o cliente consiga concluir: descobrir o produto, criar uma conta, passar pelo onboarding, criar o recurso principal, receber o resultado e voltar depois para encontrar o mesmo estado. Isso revela os limites reais do sistema mais cedo do que uma longa lista de funcionalidades.

Documente as transições de estado junto das telas. Defina quem pode executar cada transição, quais registros mudam, quais chamadas externas ocorrem e o que acontece se uma chamada se repetir ou falhar. Um SaaS confiável é projetado em torno dessas transições, não de uma coleção de cartões no painel.

  • —Rotas públicas de aquisição e documentação.
  • —Autenticação, sessão e recuperação de conta.
  • —O objeto de domínio primário e suas regras de propriedade.
  • —Um direito de acesso vinculado à cobrança ou um limite explícito do plano gratuito.
  • —Evidências de e-mails, tarefas, auditoria e falhas.

Separar código público, código da aplicação e código exclusivo do servidor

Use grupos de rotas para dar layouts diferentes às rotas de marketing e às rotas autenticadas da aplicação, sem alterar suas URLs. Mantenha páginas e layouts como Server Components e acrescente Client Components nos formulários e controles que precisam do estado do navegador. Assim, o conteúdo público continua acessível aos robôs de busca e o painel exige menos JavaScript.

Coloque acesso ao banco de dados, autorização, adaptadores de cobrança e integrações que usam segredos em módulos exclusivos do servidor. Uma camada de acesso a dados concentra as leituras seguras em um ponto auditável. Server Actions cuidam das alterações iniciadas pela interface React; Route Handlers cuidam dos webhooks, das verificações de saúde e das interfaces HTTP usadas fora dela.

A practical SaaS route tree

src/app/
├── (marketing)/page.tsx
├── (marketing)/pricing/page.tsx
├── (auth)/login/page.tsx
├── (app)/dashboard/page.tsx
├── (app)/projects/[id]/page.tsx
├── api/stripe/webhook/route.ts
└── api/health/route.ts

src/lib/
├── auth.ts
├── dal.ts
├── db.ts
└── billing.ts

Modelar a propriedade dos dados no banco

Use um banco de dados relacional para contas, vínculos de membros, registros de domínio, direitos de acesso, eventos de webhook e tarefas que exigem transações e restrições. Dê a cada registro com proprietário uma chave de conta ou tenant. Imponha unicidade e chaves estrangeiras no banco para que a concorrência não contorne as premissas do código da aplicação.

Migrações são código de produção. Faça primeiro alterações aditivas, preencha os dados existentes separadamente quando necessário, implante código capaz de ler a estrutura transitória e remova os campos antigos depois. Uma versão não deve pressupor que todas as réplicas e tarefas mudam de esquema no mesmo instante.

Desenvolver autenticação e autorização em conjunto

Use uma biblioteca de autenticação com manutenção ativa, a menos que gerenciar hashing de senhas, fluxos dos provedores, rotação de sessões, recuperação e autenticação multifator seja central para o produto. Guarde os dados da sessão em cookies seguros HTTP-only e resolva a identidade atual no servidor.

A autenticação não concede acesso a todos os registros. Verifique a autorização na camada de acesso a dados, em cada Server Action e em cada Route Handler protegido. O Proxy pode fazer um redirecionamento otimista para requisições claramente não autenticadas, mas as verificações de segurança devem permanecer junto do acesso aos dados e das alterações.

export async function getProject(projectId: string) {
  const session = await verifySession();
  const membership = await getMembership(session.userId);

  return db.project.findFirst({
    where: {
      id: projectId,
      accountId: membership.accountId,
    },
  });
}

Tratar o faturamento como estado assíncrono

O Checkout inicia o processo de cobrança, mas não determina sozinho o estado da assinatura. Crie a Checkout Session no servidor, redirecione para o provedor e atualize os direitos de acesso locais a partir de eventos de webhook verificados. Armazene os IDs de cliente e assinatura do provedor junto da conta proprietária.

Processe novas tentativas com segurança, registrando IDs de eventos com uma restrição de unicidade. Trate explicitamente ativação, mudanças de plano, falhas de pagamento, cancelamento e exclusão. Defina quais ações exigem um direito de acesso ativo e como um período de carência afeta o acesso. A interface deve ler o estado local normalizado da cobrança, em vez de consultar o provedor em cada página.

Retirar das requisições o trabalho demorado e sujeito a novas tentativas

E-mails, importações, exportações, sincronização com provedores e geração de relatórios não devem manter uma requisição HTTP aberta. Grave uma tarefa durável ou emita um evento de workflow como parte da alteração, retorne um estado útil ao usuário e deixe um worker executar o trabalho demorado com novas tentativas e prazos.

Torne as tarefas idempotentes, registre as tentativas e diferencie falhas do provedor que permitem nova tentativa de entradas inválidas. O painel deve mostrar estados pendentes, concluídos e com falha, em vez de fingir que toda ação em segundo plano termina imediatamente.

  • —Identidade estável da tarefa e chave de deduplicação.
  • —Novas tentativas limitadas, com espera progressiva.
  • —Limites de tempo para chamadas externas.
  • —Um erro final que possa ser inspecionado e um caminho de recuperação.

Testar limites e recuperação

Use testes unitários para as regras de domínio, testes de integração para a camada de acesso a dados e as alterações, e testes de navegador para cadastro, onboarding, workflow principal e cobrança. Acrescente cenários adversos: um usuário solicita um registro de outra conta, um webhook se repete, operações concorrentes disputam uma restrição do banco e um provedor externo excede o tempo limite.

Execute a compilação de produção separadamente do lint, da verificação de tipos e dos testes. Inicie a aplicação compilada com valores de ambiente semelhantes aos de produção, aplique as migrações em uma etapa controlada e faça testes básicos do conteúdo público, das leituras autenticadas, de uma alteração, da rota de webhook e do comportamento dos controles de saúde.

Implantar o contrato de execução completo

A Adios executa o servidor de produção padrão do Next.js como um processo Node.js persistente. Assim, Server Components, Route Handlers, conexões com o banco de dados e páginas autenticadas compartilham uma versão da aplicação. O manifesto registra a compilação, o comando de inicialização, a porta, o caminho de saúde, os recursos e as referências a segredos junto do código-fonte.

Implante uma prévia, inspecione os logs de compilação e execução, verifique migrações e serviços obrigatórios e promova a versão após a rota de saúde funcionar. Os domínios personalizados e o TLS gerenciado permanecem associados à versão promovida. Se uma versão candidata não iniciar ou não informar que está saudável, suas evidências continuam disponíveis sem que ela se torne a versão pública operacional.

adios.yaml

name: northstar-saas
build_cmd: npm ci && npm run build
start_cmd: npm start

runtime:
  name: node@24
  port: 3000
  health_path: /api/health

requires:
  - db

env:
  DATABASE_URL: secret://DATABASE_URL
  AUTH_SECRET: secret://AUTH_SECRET
  STRIPE_SECRET_KEY: secret://STRIPE_SECRET_KEY
Todos os artigos