Setup Inicial: Arquitetura Zero-to-Production no Dia 1
Índice do Manual
- 1. A Preparação da Nuvem (No Navegador)
- 2. Criação da Pasta e do Next.js (O Núcleo)
- 3. A Malha Multi-Contas (direnv)
- 4. O Boilerplate Crítico: Supabase SSR
- 5. Estilização e Dark Mode Nativo
- 6. Permissão de Imagens Remotas (Next Config)
- 7. Conexão Local e Banco de Tipos Fortes
- 8. Vercel Env Sync (Baixando Variáveis)
- 9. Qualidade de Código Contínua
- 10. A Cultura de Qualidade (Testes Automatizados)
- 11. O Escudo de Segurança (.gitignore)
- 12. Ligar os Motores (Isolamento de Terminais)
- Apêndice: Aprofundamento Arquitetural
O "Dia 1" de um projeto SaaS define se você passará o próximo ano entregando funcionalidades ou resolvendo débitos técnicos. Se a fundação de um projeto for inicializada incorretamente, refatorar a autenticação de cookies ou inserir o modo escuro (Dark Mode) posteriormente custará centenas de horas de trabalho de engenharia.
Este manual contém a sequência linear obrigatória para realizar o Setup Inicial de qualquer projeto SaaS corporativo do zero, utilizando a stack Next.js (App Router) + Supabase + Shadcn.
[!TIP] A Ilusão da Bala de Prata: Esta stack resolve 95% dos problemas de desenvolvimento B2B moderno. Porém, se o seu escopo envolver relatórios pesados ou conexões de longa duração, não tente forçar a Vercel a fazer o impossível. Entenda quando delegar o peso nos bastidores lendo nosso artigo: O Limite da Bala de Prata: Quando o Next.js Precisa de um NestJS.
Para manter um padrão de excelência de nível corporativo trabalhando sozinho, você deve seguir rigorosamente estes 11 passos antes de criar a primeira tela.
1. A Preparação da Nuvem (No Navegador)
Antes de digitar qualquer comando no terminal, a infraestrutura nas nuvens precisa existir para receber o projeto. No seu navegador:
- GitHub (Controle de Versão Duplo): Crie um repositório vazio. A regra de ouro corporativa é nunca commitar direto em produção. Portanto, você deve criar e proteger duas branches principais:
main: Reflete o código exato que está no ar para os clientes (Produção).staging: Reflete o código em fase de testes (Homologação). Todo o desenvolvimento diário deve ir para a branchstagingprimeiro.- (Configuração de Limpeza): Acesse Settings > Pull Requests no seu repositório recém-criado e ative a caixa Automatically delete head branches. Isso fará com que o GitHub jogue no lixo as branches de features automaticamente assim que o código for aprovado, mantendo a nuvem limpa.
- Supabase (Backend Físico Duplo): Diferente de APIs tradicionais, o Supabase provisiona bancos PostgreSQL físicos. Criar tabelas misturando dados reais e de teste é um erro fatal. Você deve acessar o painel do Supabase e criar DOIS projetos completamente novos e distintos:
[SeuApp]-Prod: O banco de dados intocável, com dados reais dos clientes.[SeuApp]-Staging: O banco de dados onde você vai rodar testes, dropar tabelas e usar dados falsos.- Acesse as configurações (Project Settings > API) de ambos os projetos e anote em um bloco de notas as chaves
Project URLeanon public keyde cada um.
- Vercel (Frontend Unificado): Crie um novo projeto vazio apontando para o seu repositório no GitHub. Acesse a aba Settings > Environment Variables. O segredo da arquitetura Multi-Ambiente é criar chaves com o mesmo nome, mas valores diferentes dependendo do contexto. Faça o seguinte mapeamento:
- Crie a variável
NEXT_PUBLIC_SUPABASE_URLe cole a URL do seu Supabase Prod. Logo abaixo, desmarque todas as caixas e deixe apenas a opção "Production" marcada. Salve. - Crie novamente a mesma variável
NEXT_PUBLIC_SUPABASE_URL, mas agora cole a URL do seu Supabase Staging. Desmarque as outras opções e deixe apenas "Preview" marcado (você pode amarrar essa variável especificamente à sua branchstagingselecionando o Custom Branch). - Repita esse processo duplo (uma para Prod, uma para Preview) para a
NEXT_PUBLIC_SUPABASE_ANON_KEYe qualquer outra chave secreta.
- Crie a variável
[!WARNING] O Ponto Cego: Integrações Externas (Stripe, E-mails) Se o sistema de Staging simular uma "Aprovação de Compra" e disparar um e-mail para um cliente real, o estrago jurídico será colossal. Para blindar integrações, repita o processo duplo acima para todas as ferramentas externas. Insira as chaves reais da Stripe e do Resend apenas no ambiente Production. Insira as chaves de teste (
sk_test_...) no Preview (Staging). Assim, o cliente testa o fluxo de pagamento inteiro no cartão clonado da Stripe, o e-mail cai num buraco negro seguro, e a arquitetura garante 100% de paz mental para a engenharia.
2. Criação da Pasta e do Next.js (O Núcleo)
Inicialize o projeto com as configurações mais restritas e modernas, garantindo que ele será criado dentro da pasta correta.
Comandos (Execute um por um):
# 1. Crie a pasta do cliente/projeto e entre nela
mkdir meu-novo-saas
cd meu-novo-saas
# 2. Instale o Next.js dentro desta pasta
npx create-next-app@latest ./ \
--typescript \
--tailwind \
--eslint \
--app \
--src-dir \
--import-alias "@/*" \
--use-npm
- Por que
--src-dir? O código da aplicação NUNCA deve se misturar com os arquivos de configuração raiz.
3. A Malha Multi-Contas (direnv)
Se você desenvolve projetos para múltiplos clientes simultaneamente, a regra de ouro arquitetural é nunca fazer login global (supabase login ou vercel login) na sua máquina.
Agora que você já criou e entrou na pasta do projeto, você DEVE criar o arquivo .envrc na raiz desta pasta com os tokens do cliente, conforme ensinado no manual de Múltiplas Contas.
Após criar o arquivo, ative a pasta no terminal rodando:
direnv allow
4. O Boilerplate Crítico: Supabase SSR
O Next.js App Router renderiza o HTML no servidor. O SDK antigo do Supabase (@supabase/supabase-js) não consegue ler cookies de sessão dentro de Server Components. Se você ignorar este passo, seu sistema de autenticação falhará.
Passo a passo:
- Instale o pacote oficial de Server-Side Rendering:
npm install @supabase/ssr @supabase/supabase-js
- Crie imediatamente a pasta utilitária de conexão e seus 3 arquivos sagrados, copiando o código oficial da documentação do Supabase:
src/utils/supabase/server.ts(Uso exclusivo em Server Components, Server Actions e API Routes).
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll() },
setAll(keysToSet) {
try {
keysToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options))
} catch { }
},
},
}
)
}
src/utils/supabase/client.ts(Uso exclusivo em Client Components com'use client').
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
src/utils/supabase/middleware.ts(Obrigatório para manter as sessões vivas).
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({ request })
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return request.cookies.getAll() },
setAll(keysToSet) {
keysToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
supabaseResponse = NextResponse.next({ request })
keysToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options))
},
},
}
)
await supabase.auth.getUser()
return supabaseResponse
}
src/middleware.ts(Na raiz do projeto, aciona o utilitário).
import { type NextRequest } from 'next/server'
import { updateSession } from '@/utils/supabase/middleware'
export async function middleware(request: NextRequest) {
return await updateSession(request)
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}
5. Estilização e Dark Mode Nativo
Adicionar o Tailwind e o Shadcn não é suficiente. Um SaaS corporativo deve ter o modo escuro funcionando no primeiro minuto. Tentar instalar Dark Mode após criar 30 painéis destruirá seu layout.
Passo a passo:
- Inicialize o Shadcn UI:
npx shadcn@latest init
(Escolha "New York", ative as variáveis CSS e defina a cor primária apropriada). 2. Instale imediatamente o provedor de tema:
npm install next-themes
- Crie o arquivo
src/components/theme-provider.tsx:
"use client"
import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
- Envolva toda a aplicação no arquivo
src/app/layout.tsxativando as fontes modernas (InterouGeist).
import { Inter } from "next/font/google"
import { ThemeProvider } from "@/components/theme-provider"
const inter = Inter({ subsets: ["latin"] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="pt-BR" suppressHydrationWarning>
<body className={`${inter.className} antialiased`}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
)
}
[!TIP] A Psicologia do Design Corporativo: O estilo "New York" do Shadcn garante densidade de dados (tabelas e botões compactos) imitando interfaces premium como Vercel e Stripe. Já o uso de fontes como
InterouGeistelimina a aparência amadora das fontes de navegador, transmitindo uma sensação imediata de "Software Rápido e Seguro" para o usuário.
6. Permissão de Imagens Remotas (Next Config)
Quando o usuário fizer upload de um avatar para o Supabase Storage (Bucket S3), o Next.js vai bloquear o carregamento da imagem nativamente por questões de segurança (se você usar a tag <Image />).
Resolução:
Edite o arquivo raiz next.config.ts no Dia 1, adicionando a configuração de images apontando exatamente para o domínio do seu bucket.
(Onde encontro este ID? O ID_DO_SEU_PROJETO é o Reference ID de 20 letras que compõe a sua URL do Supabase, por exemplo: se a sua URL for https://xyz123.supabase.co, o seu ID é xyz123).
Copie e cole a estrutura abaixo substituindo o ID pelo do seu projeto de Produção:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "ID_DO_SEU_PROJETO.supabase.co",
port: "",
pathname: "/storage/v1/object/public/**",
},
],
},
};
export default nextConfig;
7. Conexão Local e Banco de Tipos Fortes
Não programe "às cegas". A interface precisa conhecer perfeitamente a estrutura do banco de dados na nuvem.
Passo a passo:
- Inicialize o ambiente Supabase Local no projeto:
npx supabase init
- Obrigatório: Como você já criou e ativou o seu
.envrcno Passo 3, você não precisa fazer login. - A regra de ouro (Staging-First): Linke o repositório local ao seu projeto de Staging na nuvem (NUNCA à produção neste momento):
npx supabase link --project-ref ID_DO_PROJETO_STAGING
(Por que Staging e não Local? O "local" é a própria pasta onde você está. O comando link serve para avisar à CLI qual é o destino remoto oficial dessa pasta. Quando você rodar supabase db push no futuro, ele saberá que deve enviar as alterações para a nuvem de Staging. O direnv que você configurou no Passo 3 serve apenas para te autenticar).
- Adicione o seguinte script obrigatório no
package.json:
Sempre que criar uma tabela no banco, execute"scripts": { "gen-types": "supabase gen types typescript --local > src/types/database.types.ts" }npm run gen-typespara injetar o tipo TypeScript no frontend.
8. Vercel Env Sync (Baixando Variáveis)
Chega de copiar e colar chaves secretas e errar.
- Acesse o painel da Vercel (
Settings > Environment Variables). A Vercel possui três ambientes fundamentais (Production, Preview e Development). Você deve espelhar a arquitetura do Supabase aqui:- Chaves de Produção: Insira as chaves reais (Supabase Prod, Stripe Live, Resend Live) e deixe apenas o checkbox "Production" marcado.
- Chaves de Staging (Homologação): Insira as chaves do seu banco de testes (Supabase Staging, Stripe Test) e deixe apenas o checkbox "Preview" marcado (Dica: Logo abaixo do checkbox Preview, clique em "All Preview Branches", mude para "Select Custom Branch" e digite
stagingpara que o painel da Vercel isole essas chaves apenas nessa branch). - Chaves de Development (Local): Marque também o checkbox "Development" junto com o Preview para que as mesmas chaves de teste sejam baixadas para a máquina dos desenvolvedores quando rodarem o Vercel CLI localmente.
- Obrigatório: Abra o seu arquivo
.envrc(criado no Passo 3) e adicione a nova linhaexport VERCEL_TOKEN="sua_chave_vercel", rodandodirenv allowno terminal em seguida para carregar. - No terminal local, linke o projeto:
vercel link
- Baixe as chaves do ambiente de Staging (Preview) para a sua máquina:
vercel env pull .env.local --environment=preview
- Ligando o Banco Local: Para realizar o sequestro local das variáveis na próxima etapa, você precisa saber quais são as chaves da sua máquina. Abra uma nova aba no terminal e rode:
npx supabase start
(O terminal imprimirá a API URL e a anon key. Copie ambas).
- Sequestro Local (Crítico): Abra o arquivo
.env.localque acabou de ser baixado pela Vercel. Substitua aNEXT_PUBLIC_SUPABASE_URLe aNEXT_PUBLIC_SUPABASE_ANON_KEYda nuvem (Staging) pelos valores que você acabou de copiar do seu banco local. Se você pular isso, seu ambiente de desenvolvimento salvará dados diretamente no banco de Staging da nuvem, destruindo o ambiente de homologação da equipe. (Nota: O arquivo.env.localjá é ignorado por padrão no.gitignorecriado pelo Next.js).
9. Qualidade de Código Contínua
Para impedir que a equipe suba "código sujo" (ex: classes do Tailwind bagunçadas):
- Instale o organizador do Tailwind e as ferramentas de Git Hooks:
npm install -D prettier prettier-plugin-tailwindcss husky lint-staged
- Crie o arquivo
.prettierrcna raiz para ativar a auto-formatação do Tailwind:
{
"plugins": ["prettier-plugin-tailwindcss"]
}
- Inicialize o Husky (ele criará a pasta
.huskyautomaticamente):
npx husky init
- Configure o escaneamento inteligente adicionando este bloco na raiz do seu
package.json:
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
]
}
- Por fim, abra o arquivo recém-criado
.husky/pre-commite substitua tudo por:
npx lint-staged
Com isso, toda vez que um desenvolvedor digitar git commit, o código será formatado e limpo automaticamente ANTES de ir para a nuvem.
10. A Cultura de Qualidade (Testes Automatizados)
Um projeto corporativo deve nascer com os robôs de teste configurados desde o Dia 1. Instalaremos o Vitest (para testes unitários) e o Playwright (para testes E2E).
1. Instalação do Vitest (Unitário):
npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react
Abra o seu package.json e adicione a linha "test": "vitest" dentro da chave "scripts".
2. Instalação do Playwright (E2E):
npx create-playwright
(Quando o instalador perguntar, escolha Typescript, coloque os testes na pasta tests e confirme a instalação dos navegadores).
3. Automação CI/CD no GitHub Actions:
Para que o Playwright atue como "Guardião" e barre códigos quebrados nos seus Pull Requests, crie o arquivo .github/workflows/playwright.yml na raiz do projeto com o código exato abaixo:
name: Playwright E2E Tests
on:
pull_request:
branches: [ staging, main ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Instalar dependências
run: npm ci
- name: Instalar Navegadores do Playwright
run: npx playwright install --with-deps
- name: Rodar testes do Playwright
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
Com isso, a infraestrutura já nasce blindada contra regressões.
11. O Escudo de Segurança (.gitignore)
Antes de darmos o primeiro commit, é crítico garantir que segredos corporativos e caches não vazem para o repositório (GitHub).
O create-next-app já ignora arquivos pesados (node_modules, .next) e variáveis de ambiente tradicionais (.env.local, .env). Porém, nossa arquitetura Híbrida exige proteções extras. Abra o arquivo .gitignore e adicione rigorosamente as seguintes linhas ao final:
# Arquivos Locais da CLI do Supabase
supabase/.temp/
# Arquivos de Configuração da CLI da Vercel
.vercel/
# Chaves de Acesso de Multi-Contas
.envrc
12. Ligar os Motores (Isolamento de Terminais)
Para que você não perca o controle visual dos logs do sistema (principalmente quando as Edge Functions ou o backend derem erro), nós evitamos a abordagem de juntar tudo num único terminal. A arquitetura de elite exige separação de contextos.
A partir de agora, sempre que sentar para trabalhar, abra duas abas de terminal na pasta do projeto:
Terminal 1 (Backend - Supabase):
supabase start
(Ele acordará o Docker, o Banco de Dados, o Auth e o Storage em background).
Terminal 2 (Frontend - Next.js):
npm run dev
Essa separação limpa garante que, se o Next.js der erro de renderização de interface, o log sujo estará apenas no Terminal 2. E quando formos debugar o Backend no futuro, usaremos terminais exclusivos para as funções (assunto que cobriremos a fundo no Manual de Edge Functions).
O Buraco Negro de E-mails (Inbucket):
Ao testar a criação de usuários ou redefinição de senhas, o Supabase Local não envia e-mails de verdade para a internet. Ele intercepta e prende todos os e-mails do sistema de autenticação em uma caixa de entrada local. Para ler esses e-mails de teste (e clicar nos links de confirmação), você deve acessar http://localhost:54324 no seu navegador.
(Nota: Ferramentas da nuvem como o Vercel KV serão acessadas automaticamente pelo Next.js usando as chaves baixadas no Passo 6).
A partir daqui, você está livre para focar apenas em construir as lógicas e telas incríveis do seu SaaS!
Como boa prática para escalar gerenciando dezenas de clientes simultâneos, jamais misturamos infraestruturas. Cada cliente possui sua própria conta isolada no Supabase e na Vercel desde o Dia 1.
Para aprender a blindar essa infraestrutura recém-nascida contra bugs antes mesmo de escrever a primeira tela, siga obrigatoriamente para o próximo capítulo: Testes Automatizados: A Cultura Pragmática B2B (Playwright e Vitest).
Apêndice: Aprofundamento Arquitetural
Se em algum momento no futuro a complexidade do seu projeto explodir (ex: você precisar criar um App Mobile em React Native compartilhando código, ou separar a equipe do Marketing da equipe do Dashboard), a fundação que você acabou de criar permite uma migração tranquila.
Para estudos avançados sobre quando e como dar esse próximo passo, leia nossa documentação sobre Monorepos: