
Neste tutorial você vai criar uma API HTTP simples para produtos usando Node.js e Express. A aplicação aceitará requisições GET, POST, PUT e DELETE, receberá e devolverá JSON e usará códigos de status adequados. O objetivo é enxergar, em um projeto pequeno, como o CRUD vira uma interface que pode ser consumida por um site, aplicativo móvel ou outro sistema.
http://localhost:3000, com endpoints para listar, consultar, cadastrar, atualizar e excluir produtos.1. Preparando o projeto
Você precisa ter o Node.js instalado. Em uma pasta vazia, abra o terminal e execute:
npm init -y
npm install expressO primeiro comando cria o arquivo package.json. O segundo instala o Express, framework que facilita a criação de rotas e respostas HTTP.
Crie um arquivo chamado server.js. Para este primeiro projeto, os dados ficarão em memória. Isso deixa o foco nas rotas. Ao reiniciar o servidor, os produtos voltam ao estado inicial; mais adiante você pode substituir o array por PostgreSQL ou outro banco.
2. Criando o servidor Express
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.json());
app.listen(PORT, () => {
console.log(`API rodando em http://localhost:${PORT}`);
});express() cria a aplicação. express.json() permite que o Express interprete corpos de requisições enviados em JSON. O listen coloca o servidor para escutar a porta 3000.
A documentação do Express define uma rota pela combinação de método HTTP + caminho + função manipuladora. Por isso, GET /produtos e POST /produtos são operações diferentes mesmo usando o mesmo caminho.
3. Criando alguns dados para trabalhar
Acrescente este array antes do app.listen:
let produtos = [
{ id: 1, nome: 'Teclado', preco: 120 },
{ id: 2, nome: 'Mouse', preco: 80 }
];
let proximoId = 3;Em um sistema real, essa responsabilidade pertence ao banco de dados. Aqui, o array funciona como um armazenamento temporário para que você consiga visualizar o ciclo completo.
4. GET: listar todos os produtos
app.get('/produtos', (req, res) => {
res.json(produtos);
});Ao acessar GET /produtos, a API responde com o array em JSON e, por padrão, status 200.
5. GET por ID: localizar um produto
app.get('/produtos/:id', (req, res) => {
const id = Number(req.params.id);
const produto = produtos.find(p => p.id === id);
if (!produto) {
return res.status(404).json({ erro: 'Produto não encontrado' });
}
res.json(produto);
});O trecho :id é um parâmetro de rota. Como parâmetros chegam como texto, usamos Number() antes da comparação. Se o recurso não existir, retornar 404 Not Found comunica melhor o resultado do que devolver um objeto vazio.
6. POST: cadastrar um produto
app.post('/produtos', (req, res) => {
const { nome, preco } = req.body;
if (!nome || typeof preco !== 'number' || preco < 0) {
return res.status(400).json({ erro: 'Informe nome e preço válido' });
}
const produto = { id: proximoId++, nome, preco };
produtos.push(produto);
res.status(201).json(produto);
});O cliente envia dados; o servidor valida antes de confiar neles. A própria documentação do Express alerta que req.body é controlado pelo usuário e deve ser validado. Quando o cadastro é criado, 201 Created é mais informativo do que um 200 genérico.
7. PUT: atualizar um produto
app.put('/produtos/:id', (req, res) => {
const id = Number(req.params.id);
const produto = produtos.find(p => p.id === id);
if (!produto) return res.status(404).json({ erro: 'Produto não encontrado' });
const { nome, preco } = req.body;
if (!nome || typeof preco !== 'number' || preco < 0) return res.status(400).json({ erro: 'Dados inválidos' });
produto.nome = nome;
produto.preco = preco;
res.json(produto);
});Neste exemplo, o PUT substitui os campos editáveis do produto. Uma API maior também pode oferecer PATCH para alterações parciais.
8. DELETE: excluir um produto
app.delete('/produtos/:id', (req, res) => {
const id = Number(req.params.id);
const indice = produtos.findIndex(p => p.id === id);
if (indice === -1) return res.status(404).json({ erro: 'Produto não encontrado' });
produtos.splice(indice, 1);
res.status(204).send();
});Quando a exclusão ocorre e não há corpo para devolver, 204 No Content é uma resposta apropriada.
9. Código completo
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.json());
let produtos = [
{ id: 1, nome: 'Teclado', preco: 120 },
{ id: 2, nome: 'Mouse', preco: 80 }
];
let proximoId = 3;
app.get('/produtos', (req, res) => res.json(produtos));
app.get('/produtos/:id', (req, res) => {
const produto = produtos.find(p => p.id === Number(req.params.id));
if (!produto) return res.status(404).json({ erro: 'Produto não encontrado' });
res.json(produto);
});
app.post('/produtos', (req, res) => {
const { nome, preco } = req.body;
if (!nome || typeof preco !== 'number' || preco < 0) return res.status(400).json({ erro: 'Dados inválidos' });
const produto = { id: proximoId++, nome, preco };
produtos.push(produto);
res.status(201).json(produto);
});
app.put('/produtos/:id', (req, res) => {
const produto = produtos.find(p => p.id === Number(req.params.id));
if (!produto) return res.status(404).json({ erro: 'Produto não encontrado' });
const { nome, preco } = req.body;
if (!nome || typeof preco !== 'number' || preco < 0) return res.status(400).json({ erro: 'Dados inválidos' });
produto.nome = nome; produto.preco = preco; res.json(produto);
});
app.delete('/produtos/:id', (req, res) => {
const indice = produtos.findIndex(p => p.id === Number(req.params.id));
if (indice === -1) return res.status(404).json({ erro: 'Produto não encontrado' });
produtos.splice(indice, 1); res.status(204).send();
});
app.listen(PORT, () => console.log(`API rodando em http://localhost:${PORT}`));
10. Como testar
Inicie o servidor:
node server.jsVocê pode testar no terminal com curl, no Postman, Insomnia ou em outro cliente HTTP.
curl http://localhost:3000/produtos
curl -X POST http://localhost:3000/produtos \
-H "Content-Type: application/json" \
-d '{"nome":"Webcam","preco":250}'
curl -X PUT http://localhost:3000/produtos/1 \
-H "Content-Type: application/json" \
-d '{"nome":"Teclado mecânico","preco":220}'
curl -X DELETE http://localhost:3000/produtos/2
CRUD e métodos HTTP lado a lado
| Objetivo | Método | Endpoint | Status comum |
|---|---|---|---|
| Listar | GET | /produtos | 200 |
| Consultar um | GET | /produtos/:id | 200 / 404 |
| Criar | POST | /produtos | 201 / 400 |
| Atualizar | PUT | /produtos/:id | 200 / 400 / 404 |
| Excluir | DELETE | /produtos/:id | 204 / 404 |
Esta API é realmente REST?
No uso cotidiano, muita gente chama qualquer API HTTP com recursos e métodos como GET, POST, PUT e DELETE de “API REST”. É uma aproximação útil para iniciantes, mas REST é mais amplo. Na formulação original de Roy Fielding, REST é um estilo arquitetural com um conjunto de restrições, incluindo interface uniforme, comunicação sem estado e possibilidade de uso de intermediários. Uma aplicação pode usar HTTP e JSON sem cumprir integralmente essas restrições.
Por isso, o projeto deste tutorial é melhor entendido como uma API HTTP orientada a recursos, no estilo REST. Essa precisão evita transformar REST em apenas uma tabela de verbos HTTP.
O que melhorar depois
Este exemplo é didático. Em produção, você deve separar rotas e regras de negócio, persistir dados em um banco, validar entradas de forma mais robusta, tratar erros de modo centralizado, configurar autenticação e autorização quando necessário, proteger segredos em variáveis de ambiente e criar testes automatizados.
Também vale conectar este tutorial aos conteúdos já publicados: revise CRUD em banco de dados, entenda a base lógica em algoritmos e use Git e GitHub para versionar o projeto.
Desafio prático
Adicione o campo estoque aos produtos. Faça a API rejeitar valores negativos. Depois crie a rota GET /produtos/baixo-estoque para devolver somente produtos com menos de 5 unidades. Pense na ordem das rotas para que baixo-estoque não seja interpretado como um ID.
Perguntas frequentes
Preciso de banco de dados para criar uma API?
Não para aprender o fluxo. Um array em memória é suficiente para experimentar rotas e HTTP. Para persistência real, use um banco.
Express é obrigatório no Node.js?
Não. O próprio Node.js possui um módulo HTTP. O Express adiciona uma camada mais conveniente para rotas, middleware e respostas.
POST e PUT são a mesma coisa?
Não. Neste tutorial, POST cria um novo recurso e PUT atualiza um recurso identificado pela URL.
Conclusão
Uma API deixa o CRUD visível como um contrato entre sistemas. Cada endpoint combina um recurso, um método HTTP, dados de entrada, regras de validação e uma resposta com status coerente. Depois de dominar esse ciclo em memória, o passo natural é ligar a mesma estrutura a um banco de dados e organizar o projeto em camadas.
Referências bibliográficas
- BROWN, Ethan. Web Development with Node and Express. 2. ed. O’Reilly Media, 2019.
- RICHARDSON, Leonard; AMUNDSEN, Mike; RUBY, Sam. RESTful Web APIs. 1. ed. O’Reilly Media, 2013.
Documentação técnica: Node.js HTTP API; Express Routing e Express API. Material acadêmico: Fielding, Roy T., Architectural Styles and the Design of Network-based Software Architectures, 2000.