API GraphQL em Node.js + TypeScript (GraphQL Yoga) para um blog — autores, posts e comentários — com o foco no que o GraphQL tem de melhor e no seu maior perigo: resolver relações aninhadas sem cair no problema N+1, usando DataLoader. Arquitetura hexagonal e cobertura de testes 100%.
{
authors {
name
posts { # 1 busca em lote, não 1 por autor
title
comments { text } # 1 busca em lote, não 1 por post
}
}
}- GraphQL de verdade — schema-first (SDL), queries e mutations, tipos
relacionados (
Author → Post → Comment) resolvidos por field resolvers. - N+1 resolvido com DataLoader — cada relação (posts de um autor, autor de um post, comentários de um post) é resolvida em uma busca em lote por nível, não uma por item. Um DataLoader novo por requisição (o cache não vaza entre requisições).
- Arquitetura hexagonal — domínio (entidades + validação) → aplicação
(
BlogService+ ports de repositório) → infraestrutura (repos em memória, camada GraphQL). O domínio não conhece o GraphQL. - Cobertura 100% — verificada na CI, incluindo os caminhos de erro (validação e "não encontrado"). Os resolvers são testados executando operações reais contra o servidor.
- GraphiQL embutido — explorador interativo em
/graphql(como o Swagger de uma REST).
- Linguagem: TypeScript · Runtime: Node.js 22
- Servidor: GraphQL Yoga · Batching: DataLoader
- Testes/cobertura: Vitest (gate de 100% via v8)
src/
├── domain/ # entidades (Author, Post, Comment) + validação
├── application/ # BlogService (casos de uso) + ports (repositórios)
└── infrastructure/
├── memory/ # repositórios em memória
└── graphql/ # schema (SDL), resolvers, DataLoaders, context
Ao pedir 10 autores e os posts de cada um, a implementação ingênua faz 1 query para os autores + 10 para os posts (uma por autor). Com o DataLoader, os 10 pedidos de "posts do autor X" são agrupados no mesmo tick e resolvidos numa única busca em lote — 2 idas ao repositório, não 11. É a diferença entre uma API GraphQL que escala e uma que derruba o banco.
npm install
npm start # http://localhost:4000/graphql (abra no navegador: GraphiQL)Exemplo (a partir da carga inicial):
curl -X POST localhost:4000/graphql -H "content-type: application/json" \
-d '{"query":"{ authors { name posts { title comments { text } } } }"}'Mutations:
mutation {
createAuthor(input: { name: "Grace Hopper" }) { id name }
}docker compose up --build| Tipo | Campos |
|---|---|
Query |
authors, author(id), posts, post(id) |
Mutation |
createAuthor, createPost, addComment |
Author |
id, name, posts |
Post |
id, title, body, author, comments |
Comment |
id, text |
npm test # Vitest com gate de 100% (lines/branches/functions/statements)
npm run typecheck # tsc --noEmitMIT.