Igniter.js: Criei um Framework Open Source para construir REST APIs 100% Type-Safe
Fala Devs! Felipe Barcelos aqui.
Caramba, quanto tempo! Quem me acompanha no Vibe Dev deve ter notado que eu dei uma sumida boa por quase um ano. Peço desculpas pela ausência repentina, mas foi por um motivo bem "dev": eu estava totalmente imerso em código, tentando resolver uma coceira que já me incomodava há tempos e que culminou na criação de algo que tô doido pra compartilhar com vocês: o Igniter.js.
E como aqui no TabNews a gente curte transparência e a vibe "building in public", achei que seria o lugar perfeito pra contar essa história e apresentar o projeto pra vocês.
A Coceira: A Complexidade no Mundo dos Micro-SaaS
Como muitos de vocês sabem, meu foco nos últimos anos tem sido construir Micro-SaaS. Lá em 2021, botei na cabeça a meta maluca de criar 12 Micro-SaaS em 12 meses (sim, um por mês!). A ideia era clara: criar múltiplas fontes de renda e, no processo, aprender MUITO.
Pra não reinventar a roda a cada projeto, comecei a extrair partes comuns: autenticação, pagamentos, gerenciamento de times, etc. Assim nasceu, de forma orgânica, o meu SaaS Boilerplate. Ele foi forjado no campo de batalha, em projetos reais, em produção. Isso me deu uma base sólida, testada e validada.
No ano passado, abri o Boilerplate pra comunidade e foi animal ver a galera usando, criando seus próprios SaaS. Mas aí veio o "pulo do gato" (ou a coceira aumentando): com a preocupação de evitar acoplamento e seguir boas práticas, incorporei conceitos como Clean Architecture, SOLID, etc. Isso é ótimo, mas percebi que, pra quem estava começando ou não tinha tanta familiaridade com arquiteturas mais complexas, o Boilerplate acabava sendo... complexo demais. A curva de aprendizado era alta e, às vezes, a rigidez da arquitetura mais atrapalhava do que ajudava na velocidade que um Micro-SaaS exige.
Putz, e agora? Como simplificar sem perder a qualidade e a organização? Como oferecer uma Developer Experience (DX) f*da, que realmente acelere o desenvolvimento?
A Ideia: E se eu Criasse Meu Próprio Framework?
Foi aí que a lâmpada acendeu (ou o desespero bateu, escolha sua versão haha). Pensei: "E se eu abstraísse essa complexidade toda num framework?". Não só pro meu Boilerplate, mas pra qualquer dev que quisesse criar um projeto full stack (especialmente com Next.js, que eu adoro) de forma mais direta, sem perder a segurança e a organização.
Eu queria algo que ficasse num meio-termo interessante:
- Tão simples de usar quanto (ou quase) os Server Actions do Next.js, mas sem as limitações deles (como a dificuldade de ter APIs REST abertas e o caching via GET).
- Com a segurança de tipos ponta-a-ponta que a gente ama no tRPC, mas gerando APIs REST de verdade, compatíveis com qualquer cliente (mobile, outras aplicações, etc.).
- Opinionado na medida certa, guiando a estrutura do projeto pra facilitar a vida, mas flexível o suficiente pra não engessar.
E o mais importante: com uma DX que desse prazer de codar!
Nasce o Igniter.js: Fogo na Produtividade! 🔥
Depois de uns 6 meses de muito código, refatoração, testes (e alguns cabelos brancos), nasceu o Igniter.js (@igniter-js/core no NPM).
A proposta central é simplificar a conexão entre seu backend e frontend, mantendo tudo 100% type-safe. Ele é construído em cima de tecnologias que a gente já curte: TypeScript, Zod para validação, e se integra lindamente com o Prisma.
Como Funciona na Prática? (Chega de Blá Blá Blá, Show me the Code!)
A melhor forma de entender é vendo, né?
-
Setup Rápido: Com a CLI (
@igniter-js/cli
), você inicia um projeto novo (Next.js ou só REST API) em segundos:# PS: Em uma pasta vazia: # Usando npx (não precisa instalar globalmente) npx @igniter-js/cli init # Ou se instalar global (npm i -g @igniter-js/cli) igniter init
Ele te pergunta o gerenciador de pacotes (bun, pnpm, npm, yarn), se quer Full Stack (com Next.js) ou só API, e se quer usar Prisma (recomendo!). Pronto, estrutura base criada!
-
Geração de Features via Prisma: Essa é uma das partes mágicas. Você modela suas entidades no
schema.prisma
:// schema.prisma model Todo { id String @id @default(uuid()) title String completed Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
Aí você roda:
npx @igniter-js/cli generate feature
A CLI lê seu schema, pergunta qual(is) model(s) você quer gerar a feature, e... boom! 💥 Ele cria toda a estrutura de pastas (
features/todo/
) com:[feature].interface.ts
: Interfaces/DTOs (Todo, CreateTodoDTO, UpdateTodoDTO, etc.) e schemas Zod baseados no seu Prisma model. Adeus, trabalho manual chato![feature].procedure.ts
: Onde mora a lógica principal (conexão com DB, regras de negócio). Pense como um Service/Repository. Você recebe ocontext
(com DB, usuário logado, etc.) e implementa seus métodos (findMany, findOne, create, update, delete).[feature].controller.ts
: Define os endpoints REST (/todos
,/todos/:id
) e asactions
(findMany, findOne, create, etc.), ligando cada action a uma procedure e definindo o método HTTP (GET, POST, PUT, DELETE) e validação Zod para query params e body.presentation/
: Pastas para componentes React e hooks específicos dessa feature, mantendo tudo organizado.
-
Conectando Tudo (Backend): No
igniter.router.ts
, você registra seu controller:// src/igniter.router.ts import { TodoController } from './features/todo/todo.controller'; import { createIgniteRouter } from '@igniter-js/core'; export const AppRouter = createIgniteRouter({ // ... outras configs como basePath: '/api/v1' controllers: { todo: TodoController, // Simples assim! }, }); export type IAppRouter = typeof AppRouter; // Exporta o tipo pro frontend
E no seu entry point (ex:
route.ts
no Next.js App Router):// src/app/api/[[...route]]/route.ts import { nextjsAdapter } from '@igniter-js/core/adapters'; import { AppRouter } from '@/igniter.router'; // Adapta as requests Next.js para o Igniter const handler = nextjsAdapter(AppRouter); export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
-
Consumindo no Frontend (Type-Safe!):
- Crie um cliente (arquivo
igniter.client.ts
):// src/igniter.client.ts import { createIgniteClient, useIgniteQueryClient } from '@igniter-js/core/client'; import type { IAppRouter } from './igniter.router'; // Importa o tipo do router! export const api = createIgniteClient<IAppRouter>(); // Hook pra usar o Query Client (invalidar cache, etc) export const useQueryClient = useIgniteQueryClient<IAppRouter>;
- Use no Server Component (RSC):
// Exemplo em uma Page Server Component import { api } from '@/igniter.client'; export default async function Home() { const todosResult = await api.todo.findMany.query({ query: { page: 1, limit: 10 }); // Type-safe! if (todosResult.error) { return <p>Erro ao buscar tarefas: {todosResult.error.message}</p>; } return ( <ul> {todosResult.data?.map((todo) => ( <li key={todo.id}>{todo.title}</li> // Autocomplete e type check! ))} </ul> ); }
- Use no Client Component com Hooks (parecido com React Query/TanStack Query):
'use client'; import { api, useQueryClient } from '@/igniter.client'; import { useForm } from 'react-hook-form'; // ... outros imports (Zod, useZodForm, etc) function TodoList() { const { data: todos, isLoading, error, refetch } = api.todo.findMany.useQuery( { refetchOnWindowFocus: true } // Opções como no React Query ); if (isLoading) return <p>Carregando...</p>; // ... renderiza a lista ... } function TodoForm() { const queryClient = useQueryClient(); const { mutate, isLoading } = api.todo.create.useMutation({ onRequest: () => { console.log('Tarefa criada!'); queryClient.invalidateQueries(['todo', 'findMany']); // Invalida o cache da listagem! Mágico! // form.reset(); }, }); // ... Lógica do form com react-hook-form e Zod ... const onSubmit = (data) => { mutate({ title: data.title }); // Body type-safe! }; // ... renderiza o form ... }
- Crie um cliente (arquivo
Percebe a simplicidade e a segurança? Se você mudar algo no backend (um tipo, um nome de parâmetro), o TypeScript vai gritar no frontend na hora! Adeus erros de dessincronização em produção.
Onde o Igniter Brilha?
- Micro-SaaS e MVPs: Velocidade e estrutura pronta pra escalar.
- Projetos Full Stack (Next.js/Node): Integração perfeita e DX otimizada.
- APIs REST Type-Safe: Quando você precisa de uma API robusta e compatível.
- Times: Padronização da estrutura de código.
Construindo Juntos!
O Igniter.js ainda é um bebê, tá na versão 0.1.x
. Tem muita coisa pra melhorar, bugs pra corrigir, features pra adicionar. E é aí que vocês entram!
O projeto é 100% Open Source no GitHub (se puderem dar uma ⭐ lá, já ajuda demais!). Quero muito ouvir o feedback de vocês:
- O que acharam da ideia? Resolve alguma dor sua?
- Testaram? Encontraram algum bug? Abram uma issue!
- Tem sugestões de features? Discutam lá nas issues ou na nossa comunidade do Vibe Dev.
- Querem contribuir com código? Melhor ainda! Dá uma olhada nas issues abertas.
Aqui tem um vídeo onde apresento e codifico isso aqui.
Minha ideia é continuar evoluindo o Igniter.js com base na necessidade da comunidade e usá-lo como base para os projetos do Vibe Dev e no SaaS Boilerplate.
E aí? O que vocês acham? Deixem seus comentários aí embaixo! 👇
Abraço e keep coding! 🚀