API HTTP para um blog com geração de posts assistida por IA. A aplicação recebe uma ideia, usa um agente (Mastra + OpenAI) para redigir título e conteúdo em Markdown, persiste rascunhos no PostgreSQL e expõe endpoints para aprovar, rejeitar e listar publicações.
| Tecnologia | Uso |
|---|---|
| Node.js 24 (LTS Krypton) | Runtime |
node:http |
Servidor HTTP nativo |
| PostgreSQL 17 | Banco de dados |
Mastra + OpenAI (gpt-4o-mini) |
Geração de conteúdo com IA |
| Zod | Validação do output estruturado |
| Postgrator | Migrations |
| Docker / Docker Compose | Infra local e imagem de produção |
Cliente HTTP
│
▼
Router (src/http/router.js)
│
├── GET /posts
├── GET /posts/:id
├── POST /posts/draft → createPostDraft (IA)
├── PATCH /posts/:id/approve
└── DELETE /posts/:id/reject
│
▼
Post Repository (PostgreSQL)
Fluxo de um post:
POST /posts/draft— cria um rascunho via IA (approved_atepublished_atnulos).PATCH /posts/:id/approve— aprova e publica o post imediatamente.DELETE /posts/:id/reject— rejeita o post (remove aprovação e publicação).GET /posts— lista apenas posts aprovados e já publicados.GET /posts/:id— retorna um post publicado pelo ID.
- Node.js 24+ (use
nvm use— versão definida em.nvmrc) - Docker e Docker Compose (para PostgreSQL local)
- Chave da API OpenAI
Copie o arquivo de exemplo e preencha os valores:
cp .env.example .env.local| Variável | Descrição | Exemplo |
|---|---|---|
API_HOST |
Host do servidor HTTP | localhost |
API_PORT |
Porta do servidor HTTP | 8080 |
API_PROTOCOL |
Protocolo (apenas informativo no log) | http |
API_KEY |
Chave para rotas administrativas | sua-chave-secreta |
OPENAI_API_KEY |
Chave da API OpenAI | sk-... |
DATABASE_URL |
Connection string do PostgreSQL | postgresql://postgres:postgres@localhost:5432/blog |
Em produção, injete essas variáveis pelo orquestrador (Docker, Kubernetes, Railway, etc.) — nunca commite arquivos
.env.
Setup completo em um comando (cria .env.local, sobe o Postgres e roda migrations):
npm install
npm run local:setupOu passo a passo:
# 1. Instalar dependências
npm install
# 2. Criar arquivo de ambiente
npm run env:setup
# Edite .env.local e defina OPENAI_API_KEY e API_KEY
# 3. Subir PostgreSQL
npm run infra:up
# 4. Rodar migrations
npm run migrate:up
# 5. Iniciar em modo desenvolvimento (hot reload)
npm run devO servidor ficará disponível em http://localhost:8080.
| Script | Descrição |
|---|---|
npm run dev |
Inicia o servidor com --watch e .env.local |
npm run start |
Inicia o servidor em modo produção |
npm run local:setup |
Setup completo do ambiente local |
npm run env:setup |
Copia .env.example → .env.local |
npm run infra:up |
Sobe o PostgreSQL via Docker Compose |
npm run infra:down |
Para e remove os containers |
npm run migrate:up |
Aplica migrations pendentes |
npm run migrate:down |
Reverte todas as migrations |
npm run build |
Constrói a imagem Docker |
npm run lint |
Executa o oxlint |
npm run lint:fix |
Corrige problemas do oxlint |
npm run format |
Formata o código com oxfmt |
npm run format:check |
Verifica formatação sem alterar arquivos |
Base URL: http://localhost:8080 (local)
Todas as respostas são application/json.
Lista posts publicados (aprovados, não rejeitados, com published_at <= agora).
Query params:
| Param | Valores | Descrição |
|---|---|---|
include |
all |
Retorna todos os posts (incluindo rascunhos). Requer autenticação. |
Exemplo — posts públicos:
curl http://localhost:8080/postsResposta 200:
{
"data": [
{
"id": "V1StGXR8_Z5jdHi6B-myT",
"title": "Título do post",
"content": "# Conteúdo em Markdown",
"published_at": "2026-06-17T12:00:00.000Z",
"created_at": "2026-06-17T11:00:00.000Z",
"approved_at": "2026-06-17T12:00:00.000Z",
"rejected_at": null
}
]
}Exemplo — todos os posts (admin):
curl -H "Authorization: Bearer sua-api-key" \
"http://localhost:8080/posts?include=all"Respostas de erro:
| Status | Condição |
|---|---|
403 |
include=all sem Authorization: Bearer <API_KEY> válido |
Retorna um post publicado pelo ID.
curl http://localhost:8080/posts/V1StGXR8_Z5jdHi6B-myTRespostas:
| Status | Corpo |
|---|---|
200 |
{ "data": { ... } } |
404 |
{ "message": "Post not found" } |
Gera um rascunho de post com IA a partir de uma ideia.
Body:
{
"idea": "Como usar Node.js para criar APIs performáticas"
}curl -X POST http://localhost:8080/posts/draft \
-H "Content-Type: application/json" \
-d '{"idea": "Como usar Node.js para criar APIs performáticas"}'Respostas:
| Status | Corpo |
|---|---|
201 |
{ "data": { ... } } — rascunho criado |
500 |
{ "message": "..." } — erro na geração ou persistência |
Aprova e publica um post imediatamente (approved_at e published_at definidos para agora).
curl -X PATCH http://localhost:8080/posts/V1StGXR8_Z5jdHi6B-myT/approveRespostas:
| Status | Corpo |
|---|---|
200 |
{ "data": { ... } } |
404 |
{ "message": "Post not found" } |
Rejeita um post (rejected_at definido; approved_at e published_at removidos).
curl -X DELETE http://localhost:8080/posts/V1StGXR8_Z5jdHi6B-myT/rejectRespostas:
| Status | Corpo |
|---|---|
200 |
{ "data": { ... } } |
404 |
{ "message": "Post not found" } |
A rota GET /posts?include=all exige o header:
Authorization: Bearer <API_KEY>
O valor deve corresponder exatamente à variável API_KEY configurada no ambiente. A comparação é feita em tempo constante para evitar timing attacks.
| Coluna | Tipo | Descrição |
|---|---|---|
id |
VARCHAR(21) |
PK (nanoid) |
title |
TEXT |
Título do post |
content |
TEXT |
Conteúdo em Markdown |
published_at |
TIMESTAMPTZ |
Data de publicação |
created_at |
TIMESTAMPTZ |
Data de criação |
approved_at |
TIMESTAMPTZ |
Data de aprovação |
rejected_at |
TIMESTAMPTZ |
Data de rejeição |
As migrations ficam em src/database/migrations/ e são executadas com Postgrator:
# Aplicar
npm run migrate:up
# Reverter tudo
npm run migrate:downEm produção, rode as migrations antes de subir a nova versão da aplicação (ou como job de deploy).
A imagem Docker é multi-stage, instala apenas dependências de produção e roda como usuário não-root:
npm run build
# equivalente a:
# docker build -t rocketseat-blog-ia-nodejs:latest .A imagem expõe a porta 8080 e define por padrão:
NODE_ENV=productionAPI_HOST=0.0.0.0API_PORT=8080
Use um serviço gerenciado (RDS, Supabase, Neon, etc.) ou um container PostgreSQL dedicado. Anote a DATABASE_URL.
Defina no ambiente de produção:
NODE_ENV=production
API_HOST=0.0.0.0
API_PORT=8080
API_PROTOCOL=https
API_KEY=<chave-secreta-forte>
OPENAI_API_KEY=sk-...
DATABASE_URL=postgresql://user:pass@host:5432/blog
Com a DATABASE_URL de produção configurada:
node node_modules/.bin/postgrator migrateOu execute o mesmo comando a partir de um container temporário com acesso ao banco.
docker build -t rocketseat-blog-ia-nodejs:latest .
# Exemplo: tag e push para um registry
docker tag rocketseat-blog-ia-nodejs:latest seu-registry/rocketseat-blog-ia-nodejs:latest
docker push seu-registry/rocketseat-blog-ia-nodejs:latestdocker run -d \
--name blog-api \
-p 8080:8080 \
-e DATABASE_URL="postgresql://user:pass@host:5432/blog" \
-e OPENAI_API_KEY="sk-..." \
-e API_KEY="sua-chave-secreta" \
rocketseat-blog-ia-nodejs:latest- PostgreSQL acessível pela aplicação
- Migrations aplicadas
-
OPENAI_API_KEYeAPI_KEYconfiguradas - Porta
8080exposta (ou mapeada via reverse proxy) - HTTPS terminado no reverse proxy (Nginx, Caddy, load balancer)
- Backups do banco configurados
.
├── src/
│ ├── index.js # Entry point do servidor
│ ├── http/
│ │ ├── router.js # Roteador HTTP
│ │ ├── read-json-body.js # Parser de body JSON
│ │ └── verify-api-key.js # Validação da API key
│ ├── routes/
│ │ └── posts.js # Rotas de posts
│ ├── repositories/
│ │ └── post-repository.js # Acesso ao banco
│ ├── services/
│ │ └── create-post-draft.js # Geração de rascunho com IA
│ ├── mastra/
│ │ ├── index.js # Configuração Mastra
│ │ └── agents/
│ │ └── post-writer-agent.js
│ └── database/
│ ├── pool.js # Pool de conexões pg
│ └── migrations/ # SQL migrations
├── docker-compose.yml # PostgreSQL local
├── Dockerfile # Imagem de produção
├── .env.example # Template de variáveis
└── package.json
ISC