Pontos-chave
- Os princípios de conceção da API REST, como URLs baseadas em recursos, endpoints no plural e formatos de erro consistentes, tornam uma API previsível e fácil de utilizar.
- Este miniprojeto cria uma API completa de CRUD para o inventário de produtos em Node.js e Express, com o «better-sqlite3» como base.
- O Postman é usado para testar todos os endpoints localmente antes de o código ser enviado para o GitHub.
- A API finalizada é implementada na hospedagem Node.js gerida pela Cloudways e é novamente testada com a URL ativa.
Todos os projetos Node.js acabam por precisar de uma API, mais cedo ou mais tarde. As aplicações front-end precisam de dados. As aplicações móveis precisam de algo com que comunicar. Outros serviços precisam de endpoints a que aceder.
Criar uma API não é complicado, pelo menos no papel. Na prática, porém, é fácil cometer erros nos pormenores. Códigos de estado errados, respostas inconsistentes, endpoints que não seguem as convenções. A API funciona tecnicamente, mas dá a sensação de estar mal feita assim que alguém tenta usá-la.
Este blogue explica o que é uma API REST, alguns princípios de conceção que vale a pena conhecer desde o início e, depois, mostra-te passo a passo como criar uma pequena API de inventário de produtos, do início ao fim. E ainda, no final, a implementação na hospedagem NodeJS gerida pela Cloudways.
- O que é uma API REST e por que usar o Node.js
- Princípios de conceção da API REST que vale a pena conhecer
- Mini-projeto: Criar a API do inventário de produtos
- Pré-requisitos
- O que vou usar
- Passo 1: Configurar o projeto Node.js
- Passo 2: Configurar a base de dados
- Passo 3: Criar a base do servidor
- Passo 4: Criar os pontos de acesso CRUD
- Passo 5: Adicionar o tratamento global de erros
- Passo 6: Testar a API com o Postman
- Passo 7: Preparar tudo para a implementação
- Passo 8: Enviar para o GitHub
- Passo 9: Implementação no Cloudways
- O momento da verdade
- Conclusão
O que é uma API REST e por que usar o Node.js
O REST é basicamente um conjunto de regras para criar APIs HTTP. Não há nada de mais misterioso do que isso.
Cada recurso (produtos, utilizadores, o que for) tem a sua própria URL. Usas os métodos HTTP para interagir com eles:
- GET lê
- A POST cria
- Atualização do PUT e do PATCH
- DELETE remove
Cada resposta vem acompanhada de um código de estado. Um código 200 e tal significa que correu tudo bem. Um código 400 e tal significa que o cliente enviou um pedido inválido. Um código 500 e tal significa que o servidor teve um problema. Os detalhes são importantes aqui, mas é basicamente isso.
O Node.js funciona bem para este tipo de coisas. O JavaScript em ambos os lados significa que se usa uma única linguagem em toda a pilha, o que é prático. Também é bom a lidar com muitas solicitações simultâneas sem ficar lento (é isso que se chama de E/S não bloqueante, se já ouviste falar desse termo).
O Express é o que a maioria das pessoas escolhe. É pequeno, discreto e faz bem uma coisa. Combina-o com uns quantos pacotes de middleware e já tens tudo o que precisas.
Princípios de conceção da API REST que vale a pena conhecer
Há algumas coisas que vale a pena acertar antes de mexeres no código. É mais fácil configurar tudo bem à primeira do que ter de alterar mais tarde.
Os endpoints são substantivos. Não são verbos.
/products está certo. /getProducts não está. O método HTTP já indica a ação, por isso a URL apenas aponta para o que se pretende.
Usa o plural para as coleções. /products para a lista, /products/:id para um produto específico. É bom que seja previsível.
Para dados relacionados, aninha as rotas. /products/:id/reviews significa «as avaliações que pertencem a este produto». Simples.
Escolhe uma convenção de nomenclatura e mantém-te fiel a ela. Seja kebab-case ou camelCase, não importa qual. O importante é não misturares as duas.
Os erros também precisam de ter um formato consistente. Se metade dos teus endpoints devolver { error: “…” } e a outra metade devolver { message: “…”, code: 400 }, quem estiver a usar a tua API vai ter de escrever código diferente para cada endpoint. Não é nada divertido.
Mini-projeto: Criar a API do inventário de produtos
Para o miniprojeto, vou criar uma API de inventário de produtos. Adicionar produtos, listá-los (filtrar por categoria), recuperar por ID, atualizar e eliminar. Funções CRUD básicas, mas com uma estrutura adequada.
No final, isto é executado localmente, é testado através do Postman e é implementado num servidor ativo.
Pré-requisitos
Antes de começares:
- Node.js instalado
- Um editor de código (o VS Code serve)
- Postman, Thunder Client ou curl para testes
- Conhecimentos básicos de JavaScript e HTTP
O que vou usar
- Node.js
- VS Code
- Express, better-sqlite3, dotenv, cors, helmet
- Postman para testes de API
Passo 1: Configurar o projeto Node.js
Preciso de me certificar de que o Node.js está a funcionar no meu computador antes de começar a escrever qualquer código.
Como estou a trabalhar num portátil do escritório com restrições de TI bastante rígidas, não consigo usar o instalador executável padrão. Em vez disso, vou descarregar o ficheiro binário autónomo do Windows diretamente do nodejs.org. Descompactei o conteúdo do ficheiro e coloquei-o diretamente no meu diretório «Downloads».


Abrir a pasta do projeto no Prompt de Comandos
A seguir, vou ao meu computador e vou criar um diretório pessoal novinho em folha para os ficheiros do meu projeto, chamado node-product-inventory-api.

Agora vou abrir a linha de comandos para poder ir direto para esse caminho:
cd C:\Users\abdulrehman\Desktop\node-product-inventory-api

Configurar o Prompt de Comando para o executável do Node
Como o meu computador não usou um instalador formal do sistema, o Prompt de Comandos ainda não tem uma referência global para o ambiente de execução do Node. Preciso de definir explicitamente o caminho usando a ferramenta da consola:
set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64
Para verificar rapidamente se o meu terminal consegue encontrar e utilizar os ficheiros do Node no meu computador, vou executar estes dois comandos de teste:
node -v npm -v
Como ambos os comandos estão a mostrar as informações da versão, posso passar à próxima etapa, que é criar o projeto Node propriamente dito.

Criar o projeto Node.js
O primeiro passo que tenho de dar é inicializar o projeto. Posso fazer isso executando o seguinte comando para gerar um ficheiro package.json:
npm init -y

Agora que já criei o ficheiro package.json, vou baixar todas as bibliotecas externas específicas de que preciso para construir o backend do meu projeto, como:
- Express para a camada de encaminhamento do servidor
- Better-sqlite3 para gerir as operações do motor local
- Dotenv para gerir configurações
- CORS para definir limites de acesso
- O Helmet vai inserir automaticamente os cabeçalhos de segurança que faltam
Eis o comando que vou executar:
npm install express better-sqlite3 dotenv cors helmet

Passo 2: Configurar a base de dados
Agora que o meu projeto Node já está criado, vou abrir o projeto no VS Code. Para isso, vou ao VS Code > Ficheiro > Abrir pasta e seleciono a pasta «node-product-inventory-api» que tenho no ambiente de trabalho.

Agora que o projeto está aberto no VS Code, posso começar a desenvolver o mini-projeto.
Primeiro, vou criar uma pasta «db» na raiz do projeto e, dentro dela, um ficheiro chamado «database.js». Estou a fazer isto porque preciso de um sítio para guardar os produtos com os quais vou interagir através da API REST.
No novo ficheiro que criei, vou colar este trecho de código:
const path = require('path');
const Database = require('better-sqlite3');
const db = new Database(path.join(__dirname, 'products.db'));
db.exec(`
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sku TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
quantity INTEGER NOT NULL DEFAULT 0,
category TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
`);
module.exports = db;

No trecho de código acima, a restrição UNIQUE no campo sku garante que cada produto tenha um SKU único. Os campos preço e quantidade estão definidos como NOT NULL, porque são obrigatórios para todos os produtos.
Passo 3: Criar a base do servidor
A seguir, vou criar um ficheiro server.js na raiz do projeto e adicionar o seguinte código:
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const db = require('./db/database');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(helmet());
app.use(cors());
app.use(express.json());
app.get('/', (req, res) => {
res.json({
status: 'ok',
message: 'Product Inventory API is running',
});
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

No trecho de código acima, usei o Helmet para definir cabeçalhos de segurança comuns. E também o CORS para permitir pedidos de outras origens.
Também usei o express.json() porque vou enviar dados JSON a partir do Postman quando estiver a testar os meus pontos finais da API.
A rota raiz vai funcionar como uma simples verificação de estado. Depois da implementação, podes aceder ao URL base para verificar se a API está a funcionar corretamente.
Passo 4: Criar os pontos de acesso CRUD
Agora, vou criar os endpoints para a operação CRUD. E cada endpoint que criar, vou adicioná-lo ao ficheiro server.js, acima da linha app.listen(…).
GET /products (com filtro de categoria opcional)
app.get('/products', (req, res) => {
const { category } = req.query;
try {
let products;
if (category) {
products = db
.prepare(
'SELECT * FROM products WHERE category = ? ORDER BY created_at DESC'
)
.all(category);
} else {
products = db
.prepare('SELECT * FROM products ORDER BY created_at DESC')
.all();
}
res.json({ count: products.length, products });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to fetch products' });
}
});
Neste trecho de código, usei o parâmetro de consulta «category». É totalmente opcional, mas se o incluir (por exemplo, /products?category=electronics), a API deve devolver apenas os produtos que correspondam. Caso contrário, vai devolver todos os produtos.
GET /products/:id
app.get('/products/:id', (req, res) => {
try {
const product = db
.prepare('SELECT * FROM products WHERE id = ?')
.get(req.params.id);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json({ product });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to fetch product' });
}
});
Uso este endpoint para recuperar um produto pelo seu ID. Se o ID não existir, a API vai devolver uma resposta 404 em vez de uma resposta vazia.
POST /products
app.post('/products', (req, res) => {
const { sku, name, description, price, quantity, category } = req.body;
if (!sku || !name || price === undefined) {
return res
.status(400)
.json({ error: 'sku, name, and price are required' });
}
try {
const result = db
.prepare(`
INSERT INTO products
(sku, name, description, price, quantity, category)
VALUES
(?, ?, ?, ?, ?, ?)
`)
.run(
sku,
name,
description || null,
price,
quantity || 0,
category || null
);
const newProduct = db
.prepare('SELECT * FROM products WHERE id = ?')
.get(result.lastInsertRowid);
res.status(201).json({ product: newProduct });
} catch (err) {
if (err.code === 'SQLITE_CONSTRAINT_UNIQUE') {
return res
.status(400)
.json({ error: 'A product with that SKU already exists' });
}
console.error(err);
res.status(500).json({ error: 'Failed to create product' });
}
});
Este endpoint vai criar um novo produto. Incluí algumas validações básicas para garantir que os campos sku, nome e preço sejam preenchidos antes de o pedido chegar à base de dados.
O código também vai verificar se há SKUs duplicados e devolver uma resposta 400 caso encontre alguma duplicata.
Se não houver erros, a API vai devolver um código 201, que é a resposta padrão para a criação bem-sucedida de um recurso.
PUT /products/:id
app.put('/products/:id', (req, res) => {
const { sku, name, description, price, quantity, category } = req.body;
const existing = db
.prepare('SELECT * FROM products WHERE id = ?')
.get(req.params.id);
if (!existing) {
return res.status(404).json({ error: 'Product not found' });
}
try {
db.prepare(`
UPDATE products
SET
sku = ?,
name = ?,
description = ?,
price = ?,
quantity = ?,
category = ?
WHERE id = ?
`).run(
sku ?? existing.sku,
name ?? existing.name,
description ?? existing.description,
price ?? existing.price,
quantity ?? existing.quantity,
category ?? existing.category,
req.params.id
);
const updated = db
.prepare('SELECT * FROM products WHERE id = ?')
.get(req.params.id);
res.json({ product: updated });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to update product' });
}
});
Este endpoint vai atualizar um produto já existente. Usei o ?? para indicar que qualquer campo que não seja incluído na solicitação mantém o seu valor atual. Assim, posso atualizar só os campos que quero alterar, em vez de enviar o objeto do produto na íntegra.
DELETE /products/:id
app.delete('/products/:id', (req, res) => {
try {
const result = db
.prepare('DELETE FROM products WHERE id = ?')
.run(req.params.id);
if (result.changes === 0) {
return res.status(404).json({ error: 'Product not found' });
}
res.json({ message: 'Product deleted' });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to delete product' });
}
});
Este endpoint vai eliminar um produto com base no seu ID. Se o ID não existir, devolve uma resposta 404. Caso contrário, devolve uma mensagem de sucesso a confirmar que o produto foi eliminado.
Passo 5: Adicionar o tratamento global de erros
Já adicionei o tratamento de erros em cada endpoint, mas, por precaução, vou também adicionar um mecanismo de fallback global para o caso de me ter escapado alguma coisa. Por isso, vou adicionar o código seguinte ao final do ficheiro server.js, mesmo antes de app.listen(…):
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Something went wrong on our end' });
});
O primeiro middleware vai devolver uma resposta 404 Not Found para qualquer rota que não esteja definida. O segundo vai detetar erros inesperados que não sejam tratados noutro local.
Passo 6: Testar a API com o Postman
Está na hora de testar isto a sério. Vou adicionar um script de arranque ao meu `package.json` para não ter de ficar sempre a escrever o comando completo do `node`:
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
}

Depois, volto ao meu Prompt de Comando e executo:
npm start

Agora, o meu servidor está a funcionar na porta 3000. Para testar cada endpoint que criei anteriormente, vou usar o Postman.
Cria um produto — envia um pedido POST para http://localhost:3000/products com um corpo em JSON:
{
"sku": "LAPTOP-001",
"name": "Silver Laptop",
"description": "A 14-inch aluminum laptop",
"price": 1299.99,
"quantity": 10,
"category": "electronics"
}
Se o pedido for bem-sucedido, vou ver uma resposta 201, o produto recém-criado, incluindo o seu ID gerado automaticamente.

A seguir, vou enviar um pedido GET para http://localhost:3000/products para recuperar todos os produtos.

Se eu adicionar um parâmetro de consulta como ?category=electronics, a API só vai devolver os produtos dessa categoria.

Depois, posso alterar o parâmetro para ?category=Portable para buscar um produto de uma categoria diferente.

A seguir, vou testar a recuperação de um único produto com um pedido GET para http://localhost:3000/products/1.

Se eu tentar um ID que não existe, como /products/999, a API vai devolver uma resposta 404.

Para testar o endpoint «Update», vou enviar um pedido PUT para http://localhost:3000/products/1 com apenas o campo «price» que quero alterar. Por exemplo, { “price”: 1199.99 }.
Agora, só o preço é atualizado, e os restantes campos mantêm-se iguais.

Por fim, vou enviar o pedido DELETE para http://localhost:3000/products/1 para remover o produto com o ID «1».

Se eu tentar recuperar o produto que foi eliminado, recebo uma resposta 404, o que confirma que ele foi eliminado anteriormente.

Ufa! Todos os endpoints CRUD estão a funcionar como esperado.
A API está a funcionar localmente? Agora põe-na em produção
Assim que os teus endpoints passarem nos testes no Postman, implementá-los não deve ser a parte difícil.
Passo 7: Preparar tudo para a implementação
Antes de enviar o meu projeto para a hospedagem Node.js gerida pela Cloudways, vou fazer algumas pequenas alterações para o preparar para a implementação.
Primeiro, vou criar um ficheiro .env na raiz do projeto e colar este código lá dentro:
NODE_ENV=development

Ainda não há nada confidencial guardado neste ficheiro, mas gosto de o preparar com antecedência. Quando implementar a aplicação, vou mudar de «desenvolvimento» para «produção».
Também vou criar um ficheiro .gitignore e colar este código lá dentro:
node_modules .env db/products.db

Excluí o ficheiro da base de dados porque o servidor de produção deve começar com uma base de dados nova, e não com os dados de exemplo que tenho usado na produção local.
Passo 8: Enviar para o GitHub
Com tudo pronto, vou enviar o projeto para o GitHub. Primeiro, vou criar um novo repositório chamado node-product-inventory-api.
Depois, vou abrir o Prompt de Comandos e executar os comandos abaixo, um de cada vez:
git init git add . git commit -m "First commit" git branch -M main git remote add origin https://github.com/abdulrehman293/node-product-inventory-api git push -u origin main
Assim que o envio estiver concluído, vou atualizar o repositório no GitHub. E agora consigo ver todos os ficheiros do projeto, com o .env, o node_modules e o ficheiro da base de dados local excluídos.

Passo 9: Implementação no Cloudways
Agora que o projeto está no GitHub, estou pronto para o implementar. Vou usar o serviço de alojamento gerido Node.js da Cloudways para a implementação.
No painel da Cloudways, vou clicar em «Node.js» no menu da esquerda e, depois, clico em «Lançar agora».

O plano Starter dá perfeitamente para algo desta dimensão.

No ecrã seguinte, vou clicar em «Ligar via Git» e iniciar sessão no GitHub.
Assim que estiver ligado, vou escolher o meu repositório «node-product-inventory-api» e clicar em «Continuar».

Agora vou escolher a predefinição do framework de configuração «Express». A versão do Node como Node 24 (LTS). E vou deixar o diretório raiz como está por predefinição.

A seguir, vou clicar em «Alterar» em «Definições de compilação e saída», definir o «Gestor de pacotes» como «npm» e deixar o «Ficheiro de entrada» definido como «server.js».

Agora, vou clicar em «Adicionar» ao lado de «Variáveis de ambiente» e adicionar «NODE_ENV» à chave e «production» ao valor.


Depois de guardar as alterações, vou clicar em «Implementar agora». O Cloudways vai começar a descarregar o código mais recente do GitHub e a instalar as dependências do projeto.

O momento da verdade
Assim que a implementação estiver concluída com sucesso, vou copiar o URL temporário do Cloudways da página «Visão geral» da aplicação e voltar ao Postman.


Antes usava a URL do localhost, mas agora vou substituí-la pela URL temporária do Cloudways para testar cada endpoint.
GET
Primeiro, vou enviar o pedido GET para a URL base. Se tudo estiver configurado corretamente, vou receber a resposta «A API de inventário de produtos está a funcionar», o que confirma que a API está ativa.

Depois vou testar o resto dos outros endpoints. Criar um produto. Listá-los. Recuperar por ID. Atualizar. Apagar. Tal como fiz no ambiente local, só que desta vez usando a URL de produção.
POST


PUT

DELETE

Agora, se eu tentar aceder ao produto que foi eliminado, vai aparecer um erro 404.

Conclusão
E assim terminamos o nosso guia sobre como criar uma API REST com o Node.js. Neste guia, expliquei o que é o REST, abordei alguns princípios de design que vale a pena conhecer e, depois, criei uma API de inventário de produtos com endpoints CRUD completos, validação e tratamento de erros.
Também testei tudo localmente com o Postman e, depois, implementei o projeto num servidor Cloudways em produção.
Já publiquei o projeto finalizado no meu GitHub, para que possam clonar e reutilizar o código. E se quiserem tentar implementar algo semelhante por conta própria, o nosso Serviço de Alojamento Gerido para Node.js trata deste fluxo de trabalho de forma super simples: ligam o vosso repositório, escolhem uma predefinição de framework e publicam a vossa aplicação.
Implementa a tua própria API REST na Cloudways
Liga o teu repositório, escolhe uma configuração pré-definida do framework e publica a tua aplicação Node.js em poucos minutos.
P. Qual é a diferença entre REST e GraphQL?
O REST usa pontos finais fixos, em que cada URL representa um recurso e os métodos HTTP definem a ação a realizar.
O GraphQL costuma usar um único ponto de acesso, o que permite que os clientes peçam exatamente os dados de que precisam numa única consulta.
O REST é, em geral, mais fácil de aprender e é amplamente suportado, enquanto o GraphQL oferece mais flexibilidade para aplicações complexas, mas tem uma curva de aprendizagem mais íngreme.
P. Preciso do Express para criar uma API REST em Node.js?
Não. Podes criar uma API REST usando o módulo http integrado do Node.js. No entanto, vais ter de tratar do encaminhamento, do middleware e da análise das solicitações por tua conta.
O Express simplifica estas tarefas, e é por isso que é o framework mais popular para criar APIs REST no Node.js. Se estiveres à procura de alternativas, vale a pena dar uma vista de olhos ao Fastify e ao Koa.
P. Como é que posso testar a minha API REST sem um frontend?
Ferramentas como o Postman e o Thunder Client permitem-te enviar pedidos GET, POST, PUT, PATCH e DELETE sem teres de criar um frontend. O Thunder Client funciona diretamente no VS Code, o que o torna uma opção prática para muitos programadores.
Se preferires a linha de comandos, também podes usar o cURL, embora seja menos prático para pedidos mais complexos.
P. Qual é a diferença entre PUT e PATCH?
O PUT destina-se a substituir um recurso na totalidade, pelo que o pedido inclui normalmente todos os campos, mesmo aqueles que não sofreram alterações.
O PATCH foi concebido para atualizações parciais, permitindo-te enviar apenas os campos que queres modificar.
Na prática, muitas APIs, incluindo a que criámos neste tutorial, usam o método PUT para atualizações parciais, deixando os campos que não precisam de ser alterados como estão.
Start Growing with Cloudways Today.
Our Clients Love us because we never compromise on these
Abdul Rehman
O Abdul é um profissional de marketing experiente em tecnologia, movido a café e criativo, que adora manter-se a par das últimas actualizações de software e gadgets tecnológicos. É também um escritor técnico competente que consegue explicar conceitos complexos de forma simples para um público alargado. Abdul gosta de partilhar os seus conhecimentos sobre a indústria da nuvem através de manuais de utilizador, documentação e publicações em blogues.