Executando verificação de segurança...
1

De Clone a Produção em Um Fim de Semana

Um guia prático para construir uma aplicação SaaS completa — com autenticação, pagamentos, chat com IA e jobs em background — partindo de uma base pré-configurada em vez de um diretório vazio.


Já comecei projetos SaaS do zero vezes o bastante pra saber como o primeiro fim de semana costuma terminar. Sexta à noite, você está empolgado. Sábado de manhã, está configurando OAuth. Sábado à tarde, debugando webhooks do Stripe. Domingo, montando templates de e-mail e se perguntando em que momento o fim de semana escapou.

E você ainda não construiu nada do produto em si.

Este guia mostra a alternativa: partir de uma base com a infraestrutura já resolvida e dedicar o fim de semana ao que realmente importa — o seu produto.

Sexta à Noite: Do Clone à Aplicação Rodando

O ponto de partida é uma aplicação funcional, com autenticação, pagamentos, banco de dados, jobs em background e funcionalidades de IA já integradas. Em vez de criar um projeto vazio, você parte de um sistema completo e começa a remover ou adaptar o que não precisa.

Acesse eden-stack.com e crie um repositório a partir do template. Depois, clone e instale:

git clone https://github.com/seu-usuario/my-saas.git
cd my-saas
bun install

Primeiro, suba o banco de dados local:

make docker-up    # Inicia Postgres + Neon proxy
make db-push      # Envia o schema para o banco

Depois, inicie o servidor de desenvolvimento:

make dev

Pronto: você tem uma aplicação rodando com landing page, fluxo de login, dashboard, configurações e página de preços. A autenticação funciona. O banco já tem tabelas. A API tem type-safety de ponta a ponta.

Tempo total: uns 10 minutos.

Sexta à Noite: Personalizando

A primeira tarefa de verdade é configurar os serviços externos. É aqui que, num projeto tradicional, você perde horas: alternando entre dashboards, copiando API keys, debugando connection strings.

O Eden Stack resolve isso com servidores MCP. Você descreve o que precisa, e o Claude provisiona tudo:

Configure meu projeto. Eu preciso de:
- Um banco Neon chamado "my-saas-prod"
- Produtos no Stripe: um plano gratuito, um Pro a $29/mês, e um Premium a $79/mês
- Um domínio no Resend para e-mails transacionais
- PostHog para analytics
- Sentry para monitoramento de erros

O Claude aciona os servidores MCP, cria os recursos e preenche o seu .env. O que antes levava mais de uma hora alternando entre dashboards agora se resolve em uns 5 minutos.

Agora é hora da identidade visual. Atualize as constantes em src/lib/brand/:

export const brand = {
  name: "My SaaS",
  tagline: "A coisa que faz a coisa",
  url: "https://my-saas.com",
  // ...
};

Atualize o copy da landing page, troque as cores no Tailwind config, e a aplicação já tem a sua cara.

Saldo da sexta: aplicação rodando, serviços configurados, marca aplicada. Você não escreveu uma linha de código de infraestrutura.

Sábado de Manhã: Sua Primeira Feature

Aqui começa a parte boa. Agora você está construindo produto, não infraestrutura.

Digamos que o seu SaaS é uma ferramenta de gerenciamento de projetos com resumos de tarefas por IA. Você precisa de uma tabela projects, API CRUD, React hooks e UI.

Toda feature no Eden Stack segue o mesmo padrão em quatro camadas:

Camada 1: Schema

// src/lib/db/schema.ts
export const projects = pgTable("projects", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  name: text("name").notNull(),
  description: text("description"),
  userId: text("user_id").notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow(),
});
make db-push

Camada 2: API

// src/server/routes/projects.ts
export const projectRoutes = new Elysia({ prefix: "/projects" })
  .get("/", async ({ request }) => {
    const session = await auth.api.getSession({ headers: request.headers });
    if (!session) throw new Error("Unauthorized");

    const results = await db
      .select()
      .from(projects)
      .where(eq(projects.userId, session.user.id));

    return { data: results };
  })
  .post("/", async ({ body, request }) => {
    const session = await auth.api.getSession({ headers: request.headers });
    if (!session) throw new Error("Unauthorized");

    const project = await db.insert(projects).values({
      name: body.name,
      description: body.description,
      userId: session.user.id,
    }).returning();

    return { data: project[0] };
  });

Registre em src/server/api.ts, e o Eden Treaty gera o client tipado automaticamente.

Camada 3: Hooks

// src/hooks/use-projects.ts
export const useProjects = () =>
  useQuery({
    queryKey: ["projects"],
    queryFn: () => getTreaty().projects.get().then((r) => r.data),
  });

export const useCreateProject = () =>
  useMutation({
    mutationFn: (data: { name: string; description?: string }) =>
      getTreaty().projects.post(data),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ["projects"] }),
  });

Camada 4: UI

Monte o componente usando os hooks. React padrão, com gerenciamento de estado via TanStack Query.

O padrão é o mesmo para qualquer feature que você adicionar. Schema, API, hooks, UI. Depois de fazer duas vezes, vira automático.

Ou simplesmente descreva para o Claude. Ele conhece o padrão de quatro camadas. Peça "adicione uma feature de projetos com CRUD" e ele gera todas as camadas seguindo as convenções do codebase.

Sábado à Tarde: A Parte de IA

Seu SaaS precisa de resumos de tarefas por IA. A infraestrutura do chatbot já está pronta. Você está estendendo, não construindo do zero.

O chatbot agêntico usa o Vercel AI SDK com tool calling. O Claude decide quando invocar ferramentas com base na intenção do usuário. Basta adicionar uma nova:

const summarizeProjectTool = tool({
  description: "Summarize all tasks in a project",
  parameters: z.object({
    projectId: z.string().describe("The project to summarize"),
  }),
  execute: async ({ projectId }) => {
    const tasks = await db
      .select()
      .from(projectTasks)
      .where(eq(projectTasks.projectId, projectId));

    return { tasks: tasks.map((t) => ({ title: t.title, status: t.status })) };
  },
});

Registre a ferramenta, e agora, quando um usuário perguntar "resuma meu projeto", o Claude aciona essa tool, busca as tarefas e gera o resumo. Sem lógica de roteamento manual — o modelo cuida da classificação de intenção.

Para operações mais demoradas, como analisar a velocidade de um projeto ao longo do tempo, você usa o Inngest para execução durável em background:

export const analyzeVelocity = inngest.createFunction(
  { id: "analyze-velocity" },
  { event: "project/analyze" },
  async ({ event, step }) => {
    const tasks = await step.run("fetch-tasks", () =>
      getCompletedTasks(event.data.projectId)
    );

    const analysis = await step.run("generate-analysis", () =>
      generateVelocityReport(tasks)
    );

    await step.run("save-report", () =>
      saveReport(event.data.projectId, analysis)
    );

    return analysis;
  }
);

Cada step tem checkpoint. Se a chamada de LLM em generate-analysis der timeout, o Inngest faz retry apenas daquele step — não da função inteira.

Domingo: Deploy

A aplicação vai para a Vercel. O banco já está no Neon. Os jobs em background rodam na cloud do Inngest.

# Faça push para o GitHub, conecte à Vercel
git push origin main

Configure as variáveis de ambiente no dashboard da Vercel (copie do seu .env) e pronto: está no ar.

Para webhooks do Stripe em produção, aponte a URL do webhook para o seu deploy na Vercel e atualize o webhook secret. Para o Inngest, adicione a signing key de produção. Nos dois casos, é uma única linha de configuração.

Tempo total de deploy: uns 15 minutos.

O Que Você Não Precisou Fazer

Toda essa infraestrutura ficou de fora do seu fim de semana:

  • OAuth com gerenciamento de sessão e refresh de token
  • Checkout do Stripe, tratamento de webhooks e ciclo de vida de assinaturas
  • Templates de e-mail com React Email + Resend
  • Fila de jobs em background com retries e checkpointing
  • Monitoramento de erros e source maps com Sentry
  • Analytics com tracking de eventos e feature flags
  • Geração de client de API com type-safety
  • Migrations e gerenciamento de schema do banco
  • Middleware de rotas protegidas
  • Gerenciamento de workspaces multi-tenant

Tudo isso funciona. Tudo isso trata seus edge cases. Você não precisou pensar em nada disso porque já estava resolvido.

O Tradeoff

Sejamos honestos sobre o custo dessa abordagem.

Você está herdando complexidade. Mesmo com cada integração isolada em src/lib/, ainda é código que você vai precisar entender quando algo quebrar. Se você nunca usou Inngest ou Elysia, tem uma curva de aprendizado pela frente.

Talvez você não precise de tudo. Se o seu SaaS não usa jobs em background nem funcionalidades de IA, parte da infraestrutura é peso morto até você remover. A arquitetura facilita a remoção, mas ainda é um passo a mais.

Você está comprando um conjunto de opiniões. TanStack Start em vez de Next.js. Elysia em vez de Express. Drizzle em vez de Prisma. São boas escolhas (na minha visão), mas são escolhas — e vêm com consequências.

A alternativa é começar do zero e tomar cada decisão por conta própria. É um caminho válido. Só leva mais tempo, e a maioria dessas decisões vai te levar ao mesmo lugar de qualquer forma.

A Conta Final

Um fim de semana com o Eden Stack: clone, configure os serviços, construa sua feature principal, adicione IA, faça deploy. No final, você entrega uma aplicação de produção com autenticação, pagamentos e funcionalidades de IA.

Um fim de semana partindo do zero: você tem OAuth funcionando e, com sorte, um banco conectado. Pagamentos e IA ficam para o próximo fim de semana.

A diferença vai se acumulando. Toda feature que você adiciona segue o mesmo padrão de quatro camadas e leva minutos em vez de horas. Depois de um mês, a distância não é incremental — é de outra categoria.


Magnus Rødseth é desenvolvedor e consultor baseado em Oslo, e criador do Eden Stack, um starter kit de produção para aplicações SaaS AI-native.

Carregando publicação patrocinada...