API HTTP desenvolvida em Node.js puro (sem frameworks como Express, Fastify ou NestJS), com persistência em PostgreSQL, controle de migrações via Postgrator, geração de conteúdo com IA, validação de esquemas com Zod, identificadores com Nanoid e suíte completa de testes end-to-end com Supertest.
- Servidor HTTP Nativo: Utiliza exclusivamente o módulo nativo
node:http. - Parser Nativo de JSON: Leitura e parsing de stream no corpo das requisições com proteção de tamanho de payload (1MB) e validação de sintaxe (
400 Bad Request). - Geração de Posts com IA: Recebe ideias de tópicos e gera título e conteúdo estruturado em Markdown (com seções de introdução, desenvolvimento e conclusão).
- Validação com Zod: Validação estrita tanto da entrada da requisição quanto do retorno da IA antes da persistência.
- Ciclo de Vida de Posts:
Rascunho: criado via IA; não é público (published_at,approved_aterejected_atiniciam comonull).Aprovação & Publicação: defineapproved_atepublished_atcom a data atual e torna o post acessível publicamente.Rejeição: definerejected_atcom a data atual e remove status de publicação.Re-aprovação: permite re-aprovar posts previamente rejeitados, limpandorejected_atde volta paranull.
- Persistência Relacional: Banco de dados PostgreSQL com pool de conexões resiliente (
pg) e encerramento gracioso (graceful shutdown). - Migrações Automatizadas: Versionamento do schema do banco com
postgrator(suporte a migrate e rollback). - Testes End-to-End: 100% dos fluxos e regras de negócio cobertos por testes automatizados com
superteste o test runner nativonode:test.
- Runtime: Node.js (v20+)
- Módulo HTTP: Nativo (
node:http) - Banco de Dados: PostgreSQL (v15)
- Driver PostgreSQL:
pg - Gerenciador de Migrações:
postgrator - Geração de IDs:
nanoid - Validação de Dados:
zod - Testes Automatizados:
supertestenode:test - Ambiente em Containers: Docker & Docker Compose
API-Blog-NodeJS/
├── assets/
│ └── banner.png # Banner ilustrativo do projeto
├── docker-compose.yml # Definição do container PostgreSQL
├── migrations/ # Arquivos de migração SQL (do/undo)
│ ├── 001.do.create-posts-table.sql
│ └── 001.undo.create-posts-table.sql
├── src/
│ ├── db/
│ │ ├── index.js # Pool de conexões do PostgreSQL (pg)
│ │ └── migrate.js # Executor programático de migrações (postgrator)
│ ├── repositories/
│ │ └── postsRepository.js # Operações de banco de dados para posts
│ ├── routes/
│ │ └── postsRoutes.js # Rotas e controladores do módulo de posts
│ ├── services/
│ │ └── aiService.js # Integração com IA e validação com Zod
│ ├── utils/
│ │ └── httpHelper.js # Utilitário para envio de respostas JSON
│ └── server.js # Servidor HTTP nativo e inicialização
├── tests/
│ └── posts.e2e.test.js # Testes end-to-end com Supertest
├── .env.example # Exemplo de variáveis de ambiente
├── .gitignore # Arquivos ignorados pelo Git
├── package.json # Dependências e scripts do projeto
└── README.md # Documentação do projeto
- Node.js v20 ou superior instalado.
- Docker e Docker Compose instalados e em execução.
git clone <URL_DO_REPOSITORIO>
cd API-Blog-NodeJS
npm installCopie o arquivo .env.example para .env:
cp .env.example .envConfiguração padrão no .env:
PORT=3000
DATABASE_URL=postgres://blog_user:blog_pass@localhost:5433/blog_db
IA_API_KEY=coloque_sua_chave_aquiNota: A porta externa padrão mapeada no
docker-compose.ymlé5433(mapeada para5432no container) para evitar conflitos com outros serviços locais de banco de dados.
Suba o container do PostgreSQL em segundo plano:
docker compose up -dExecute as migrações com o Postgrator para criar as tabelas e índices necessários:
npm run migrate(Opcional: para reverter a última migração, execute npm run migrate:undo).
Modo de desenvolvimento (recarregamento automático com --watch nativo do Node):
npm run devModo de produção:
npm startO servidor estará disponível em: http://localhost:3000.
[ Ideia ]
│
▼ POST /posts/draft
┌──────────────┐
│ RASCUNHO │ ◄─── (approved_at: null, published_at: null, rejected_at: null)
└──────┬───────┘ • Oculto na listagem pública
│ • Visível em GET /posts?include=all
│ • GET /posts/:id retorna 404
│
├── (DELETE /posts/:id/reject) ──┐
│ │
▼ PATCH /posts/:id/approve │
┌────────────────────────┐ │
│ APROVADO & PUBLICADO │ │
└───────────┬────────────┘ │
│ • approved_at & published_at │
│ • rejected_at: null │
│ • Listado em GET /posts │
│ • GET /posts/:id retorna 200 │
│ │
▼ DELETE /posts/:id/reject │
┌──────────────┐ │
│ REJEITADO │ ◄──────────────────────┘
└──────┬───────┘
│ • rejected_at preenchido
│ • approved_at: null & published_at: null
│ • Oculto na listagem pública
│ • Visível em GET /posts?include=all
│
▼ PATCH /posts/:id/approve
[ RE-APROVADO ] ──► Volta a ser público
Todas as respostas da API utilizam Content-Type: application/json; charset=utf-8.
Verifica a disponibilidade da API (healthcheck).
- Resposta (200 OK):
{
"status": "ok",
"message": "API Blog Node.js online"
}Gera um rascunho de post utilizando IA a partir de uma ideia.
- Corpo da Requisição (JSON):
{
"idea": "Arquitetura Limpa e microsserviços em Node.js"
}- Exemplo com cURL:
curl -X POST http://localhost:3000/posts/draft \
-H "Content-Type: application/json" \
-d '{"idea": "Arquitetura Limpa e microsserviços em Node.js"}'- Resposta de Sucesso (201 Created):
{
"message": "Rascunho criado com sucesso.",
"post": {
"id": "V1StGXR8_Z5jdHi6B-myT",
"title": "Arquitetura Limpa e microsserviços em Node.js: O Guia Completo e Definitivo",
"content": "## Introdução\n\nNo cenário atual de tecnologia...",
"created_at": "2026-10-15T22:30:00.000Z",
"approved_at": null,
"published_at": null,
"rejected_at": null
}
}- Resposta de Erro (400 Bad Request):
{
"error": "Bad Request",
"message": "Dados inválidos na requisição.",
"details": {
"idea": [
"A ideia deve ter pelo menos 3 caracteres."
]
}
}Lista apenas posts que estão aprovados e publicados (published_at <= NOW(), approved_at preenchido e rejected_at nulo).
- Exemplo com cURL:
curl http://localhost:3000/posts- Resposta (200 OK):
{
"posts": [
{
"id": "V1StGXR8_Z5jdHi6B-myT",
"title": "Arquitetura Limpa e microsserviços em Node.js",
"content": "## Introdução\n...",
"created_at": "2026-10-15T22:30:00.000Z",
"approved_at": "2026-10-15T22:35:00.000Z",
"published_at": "2026-10-15T22:35:00.000Z",
"rejected_at": null
}
]
}Lista todos os posts registrados no banco de dados, independentemente do status (rascunhos, aprovados e rejeitados).
- Exemplo com cURL:
curl "http://localhost:3000/posts?include=all"- Resposta (200 OK): Retorna o array completo com todos os posts.
Busca um post pelo identificador único. Apenas posts públicos são retornados.
- Exemplo com cURL:
curl http://localhost:3000/posts/V1StGXR8_Z5jdHi6B-myT- Resposta de Sucesso (200 OK):
{
"post": {
"id": "V1StGXR8_Z5jdHi6B-myT",
"title": "Arquitetura Limpa e microsserviços em Node.js",
"content": "## Introdução\n...",
"created_at": "2026-10-15T22:30:00.000Z",
"approved_at": "2026-10-15T22:35:00.000Z",
"published_at": "2026-10-15T22:35:00.000Z",
"rejected_at": null
}
}- Resposta se o post for rascunho, rejeitado ou não existir (404 Not Found):
{
"error": "Not Found",
"message": "Post não encontrado ou não está público."
}Aprova e publica o post. Define approved_at e published_at com a data atual e redefine rejected_at para null.
- Exemplo com cURL:
curl -X PATCH http://localhost:3000/posts/V1StGXR8_Z5jdHi6B-myT/approve- Resposta (200 OK):
{
"message": "Post aprovado e publicado com sucesso.",
"post": {
"id": "V1StGXR8_Z5jdHi6B-myT",
"title": "Arquitetura Limpa e microsserviços em Node.js",
"content": "...",
"created_at": "2026-10-15T22:30:00.000Z",
"approved_at": "2026-10-15T22:35:00.000Z",
"published_at": "2026-10-15T22:35:00.000Z",
"rejected_at": null
}
}Rejeita o post. Define rejected_at com a data atual e anula os campos approved_at e published_at.
- Exemplo com cURL:
curl -X DELETE http://localhost:3000/posts/V1StGXR8_Z5jdHi6B-myT/reject- Resposta (200 OK):
{
"message": "Post rejeitado com sucesso.",
"post": {
"id": "V1StGXR8_Z5jdHi6B-myT",
"title": "Arquitetura Limpa e microsserviços em Node.js",
"content": "...",
"created_at": "2026-10-15T22:30:00.000Z",
"approved_at": null,
"published_at": null,
"rejected_at": "2026-10-15T22:40:00.000Z"
}
}A aplicação conta com uma suíte de testes ponta a ponta (E2E) utilizando Supertest e o test runner nativo do Node.js (node:test).
Para executar toda a suíte de testes:
npm test- Roteador HTTP nativo: Verificação de rotas, códigos de status e respostas JSON.
- Parsing de Corpo JSON: Validação de entradas válidas, tratamento de JSON corrompido (
400) e rotas não encontradas (404). - Geração de Rascunhos: Criação com IA, estrutura de Markdown e verificação dos campos nulos.
- Ciclo de Vida do Post:
- Ocultamento de rascunhos em buscas públicas.
- Exibição de rascunhos na listagem geral (
?include=all). - Aprovação e publicação com liberação do acesso público.
- Rejeição e imediata exclusão das consultas públicas.
- Re-aprovação de posts rejeitados.
- Rejeição direta de rascunhos.
- Respostas
404para tentativas de aprovação/rejeição em IDs inexistentes.
- Encerramento de Conexões: Liberação e encerramento seguro do pool PostgreSQL ao término dos testes.
Este projeto está sob a licença ISC.
