Node.js: Design de API REST
Última atualização: 2026-08-26
A equipe da Alice está desenvolvendo tanto o front-end quanto o back-end de um sistema de gerenciamento de tarefas. Os desenvolvedores de front-end estão reclamando que não sabem quais APIs estão disponíveis, quais métodos HTTP devem usar nem em quais formatos as respostas serão fornecidas. Os desenvolvedores de back-end estão igualmente frustrados — para a mesma operação de “atualização de tarefa”, alguns usam POST, outros usam PUT e outros ainda usam PATCH, resultando em uma grande variedade de formatos de resposta. A colaboração se transformou em caos.
Alice decidiu adotar a especificação REST. Depois que a equipe padronizou a nomenclatura dos recursos, o mapeamento de métodos, os códigos de status e os formatos de resposta, a API tornou-se clara e previsível; a equipe de front-end não precisou mais consultar repetidamente a documentação da API, e a eficiência da colaboração dobrou.
1. O que você vai aprender
- Os quatro princípios fundamentais da arquitetura REST
- Mapeamento correto das operações CRUD para os métodos HTTP
- O que fazer e o que não fazer no projeto de URLs RESTful
- Estratégias para a seleção de códigos de status HTTP
- Especificações JSON para solicitações e respostas
- Três estratégias para o controle de versões de APIs
- O Modelo de Maturidade REST e o HATEOAS
2. Princípios da arquitetura REST
(1) O que é REST?
REST (Representational State Transfer) é um estilo de arquitetura de software proposto por Roy Fielding em 2000. Ele define um conjunto de restrições para o projeto de interfaces de aplicativos da web. O REST não é um protocolo nem um padrão, mas sim uma filosofia de projeto.
(2) Quatro princípios fundamentais
| Princípio | Significado | Exemplo |
|---|---|---|
| Recurso | Tudo é um recurso, identificado por uma URL | /tasks, /users/42 |
| Camada de representação | O formato no qual um recurso é representado, como JSON | {"id": 1, "タイトル": "Learn REST"} |
| Sem estado | Cada solicitação contém todas as informações necessárias | As solicitações incluem um token e não dependem de sessões |
| Interface padronizada | Operar recursos usando métodos HTTP padrão | GET para ler, POST para criar, DELETE para excluir |
▶ Exemplo: Sem estado vs. Com estado
// Stateful: Depends on server session (not RESTful)
app.post('/login', (req, res) => {
req.session.userId = 42; // Server State Saving
res.send('logged in');
});
app.get('/profile', (req, res) => {
const userId = req.session.userId; // Depends on the server status
res.json({ id: userId, name: 'Alice' });
});
// Stateless:Include authentication information with every request(RESTful)
app.get('/profile', (req, res) => {
const userId = verifyToken(req.headers.authorization);
res.json({ id: userId, name: 'Alice' });
});
3. CRUD e mapeamentos de métodos HTTP
(1) Relação de mapeamento padrão
A ideia central do REST é usar métodos HTTP para expressar a intenção das operações sobre os recursos, em vez de incorporar verbos de ação nas URLs.
| Operações CRUD | Métodos HTTP | Caminhos | Idempotência | Segurança |
|---|---|---|---|---|
| Criar | POST | /tasks |
Não | Não |
| Ler (Lista) | GET | /tasks |
Sim | Sim |
| Leitura (única) | GET | /tasks/42 |
Sim | Sim |
| Atualização (Completa) | PUT | /tasks/42 |
Sim | Não |
| Atualização (parcial) | PATCH | /tasks/42 |
Não | Não |
| Excluir | EXCLUIR | /tasks/42 |
Sim | Não |
(2) Uma explicação detalhada sobre a idempotência
Idempotência significa que executar a mesma solicitação uma vez tem o mesmo efeito que executá-la várias vezes. GET, PUT e DELETE são idempotentes, enquanto POST e PATCH não são.
▶ Exemplo: Diferenças na idempotência entre PUT e POST
// POST: Creates a new resource on every call (Non-idempotent)
// 1st POST /tasks → Create id=1
// 2nd POST /tasks → Create id=2
app.post('/tasks', (req, res) => {
const task = { id: nextId++, ...req.body };
tasks.push(task);
res.status(201).json(task);
});
// PUT: Replaces the same resource on every call (Idempotent)
// 1st PUT /tasks/1 → Replace id=1
// 2nd PUT /tasks/1 → Replace id=1 (The results are the same)
app.put('/tasks/:id', (req, res) => {
const idx = tasks.findIndex(t => t.id === parseInt(req.params.id));
if (idx === -1) return res.status(404).json({ error: 'Not found' });
tasks[idx] = { id: parseInt(req.params.id), ...req.body };
res.json(tasks[idx]);
});
▶ Exemplo: Atualização parcial com PATCH
// PATCH:Modify only the fields provided
app.patch('/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).json({ error: 'Not found' });
Object.assign(task, req.body);
res.json(task);
});
// Request:Modify only status Field
// PATCH /tasks/1 {"status": "done"}
// Raw Data:{"id":1,"title":"Learn REST","status":"pending"}
// Results:{"id":1,"title":"Learn REST","status":"done"}
4. Diretrizes para a criação de URLs
(1) Regras básicas
O projeto de URLs RESTful segue um conjunto de convenções que tornam as APIs intuitivas e fáceis de ler.
| Regra | Correto ✅ | Incorreto ❌ |
|---|---|---|
| Use substantivos, não verbos | GET /tasks |
GET /getTasks |
| Use o plural, não o singular | /tasks |
/task |
| Representação de relações por meio de aninhamento | /users/42/tasks |
/tasksByUser?userId=42 |
| No máximo 3 níveis | /users/42/tasks/1 |
/orgs/1/teams/2/users/42/tasks |
| Filtrar por parâmetros de consulta | /tasks?status=done |
/doneTasks |
| Usando o kebab-case | /task-アイテム |
/taskItems |
(2) Projeto de recursos aninhados
Recursos aninhados indicam uma relação hierárquica. Use caminhos aninhados quando um recurso filho não puder existir independentemente de seu recurso pai.
▶ Exemplo: Estrutura de URLs para um sistema de gerenciamento de tarefas
# Mission Resources
GET /tasks # Get the task list
POST /tasks # Create a New Task
GET /tasks/42 # Get a Single Task
PUT /tasks/42 # Full Update Task
PATCH /tasks/42 # Partial Update Task
DELETE /tasks/42 # Delete Task
# Comments on the Assignment(Nested Resources)
GET /tasks/42/comments # Get a Task42List of comments
POST /tasks/42/comments # For the mission42Add a comment
GET /tasks/42/comments/7 # Get a Task42Comments on7
DELETE /tasks/42/comments/7 # Delete Comment7
# Filtering and Pagination
GET /tasks?status=done&page=2&limit=20
GET /tasks?sort=-created_at # Sort by creation date in reverse chronological order
▶ Exemplo: Usos comuns dos parâmetros de consulta em URLs
app.get('/tasks', (req, res) => {
let result = [...tasks];
// Filter
if (req.query.status) {
result = result.filter(t => t.status === req.query.status);
}
// Sort
if (req.query.sort) {
const field = req.query.sort.startsWith('-')
? req.query.sort.slice(1)
: req.query.sort;
const order = req.query.sort.startsWith('-') ? -1 : 1;
result.sort((a, b) => (a[field] > b[field] ? order : -order));
}
// Pagination
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 20;
const start = (page - 1) * limit;
result = result.slice(start, start + limit);
res.json({
data: result,
page,
limit,
total: tasks.length
});
});
5. Escolha dos códigos de status HTTP
(1) Classificação e seleção de códigos de status
Os códigos de status HTTP são sinais essenciais para a comunicação entre APIs REST e clientes. Ao escolher o código de status correto, os clientes podem compreender com precisão o resultado de uma solicitação.
| Cenário | Código de status | Significado | Descrição |
|---|---|---|---|
| Recurso recuperado com sucesso | 200 OK | Solicitação bem-sucedida | Retornado quando GET/PUT/PATCH é bem-sucedido |
| Recurso criado com sucesso | 201 Criado | Recurso criado | Retornado após uma solicitação POST bem-sucedida; deve incluir um cabeçalho Location |
| Recurso excluído com sucesso | 204 Sem conteúdo | Sem conteúdo | Retornado quando a operação DELETE é bem-sucedida; sem corpo de resposta |
| Parâmetros de solicitação inválidos | 400 Solicitação inválida | Erro de sintaxe na solicitação do cliente | Campos obrigatórios ausentes, formato inválido |
| Não autenticado | 401 Não autorizado | Nenhuma informação de autenticação fornecida | Token ausente ou inválido |
| Sem permissão | 403 Proibido | Autenticado, mas sem permissão | Usuário comum acessando a interface de administração |
| O recurso não existe | 404 Não encontrado | O recurso solicitado não existe | O recurso com este ID não foi encontrado |
| Erro do servidor | 500 Erro interno do servidor | Erro interno do servidor | Exceção não capturada |
(2) Erros comuns: uso incorreto dos códigos de status
▶ Exemplo: Uso correto dos códigos de status
// Create a Resource → 201 + Location
app.post('/tasks', (req, res) => {
const task = { id: nextId++, ...req.body };
tasks.push(task);
res.status(201)
.location(`/tasks/${task.id}`)
.json(task);
});
// Delete Resource → 204(Non-responsive body)
app.delete('/tasks/:id', (req, res) => {
const idx = tasks.findIndex(t => t.id === parseInt(req.params.id));
if (idx === -1) return res.status(404).json({ error: 'Not found' });
tasks.splice(idx, 1);
res.status(204).end();
});
// Verification Failed → 400 + Error Details
app.post('/tasks', (req, res) => {
if (!req.body.title) {
return res.status(400).json({
error: 'Validation failed',
details: [{ field: 'title', message: 'Title is required' }]
});
}
// ...
});
6. Formatos de solicitação e resposta
(1) Convenções da especificação JSON
| Convenção | Padrão | Exemplo |
|---|---|---|
| Nome do campo | camelCase | createdAt, taskId |
| Formato de data | ISO 8601 | 2025-07-03T10:30:00Z |
| Resposta da lista | Inclui dados + informações de paginação | {"data": [...], "total": 100} |
| Resposta de erro | Inclui o erro + detalhes | {"error": "Not found", "details": [...]} |
| Tratamento de valores nulos | Use null em vez de omitir o campo |
{"description": null} |
| Tipo de ID | String (para evitar problemas de precisão) | {"id": "42"} |
▶ Exemplo: Resposta com lista padronizada
app.get('/tasks', (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 20;
const start = (page - 1) * limit;
const data = tasks.slice(start, start + limit);
res.json({
data,
pagination: {
page,
limit,
total: tasks.length,
totalPages: Math.ceil(tasks.length / limit)
}
});
});
// Response Example
{
"data": [
{
"id": "1",
"タイトル": "Learn REST",
"status": "pending",
"createdAt": "2025-07-03T10:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1,
"totalPages": 1
}
}
▶ Exemplo: Resposta de erro padronizada
// Unified Error Handling Middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || 'Internal Server Error',
details: err.details || [],
requestId: req.id,
timestamp: new Date().toISOString()
});
});
// Custom Error Classes
class ApiError extends Error {
constructor(status, message, details = []) {
super(message);
this.status = status;
this.details = details;
}
}
// Usage
app.get('/tasks/:id', (req, res, next) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) {
return next(new ApiError(404, 'Task not found', [
{ field: 'id', message: `No task with id ${req.params.id}` }
]));
}
res.json(task);
});
// Examples of Error Responses
{
"error": "Task not found",
"details": [
{ "field": "id", "message": "No task with id 999" }
],
"requestId": "req-a1b2c3",
"timestamp": "2025-07-03T10:30:00Z"
}
7. Estratégia de versionamento da API
(1) Comparação entre três estratégias predominantes
| Estratégia | Exemplo | Vantagens | Desvantagens | Cenários aplicáveis |
|---|---|---|---|---|
| Caminho da URL | /api/v1/tasks |
Intuitivo, pode ser testado em um navegador | URLs mais longas, o que gera polêmica entre os defensores puristas do REST | A maioria das APIs públicas |
| Cabeçalho da solicitação | Accept: application/vnd.myapi.v1+json |
URL RESTful limpa e pura | Não é intuitiva, difícil de depurar | Busca a pureza REST |
| Parâmetro de consulta | /api/tasks?version=1 |
Mais simples | Facilmente ignorado; estratégia de cache complexa | APIs internas, projetos simples |
(2) Melhores práticas para controle de versões
- A partir da v1, não omita o número da versão
- Faça a atualização para uma versão principal somente quando houver alterações que causem incompatibilidade
- As versões anteriores terão suporte por pelo menos 6 meses
- Especifique a versão atual no cabeçalho da resposta
▶ Exemplo: Implementação do controle de versão de caminhos de URL
// Routing Structure
// /api/v1/tasks → v1 Logic
// /api/v2/tasks → v2 Logic
const express = require('express');
const app = express();
// v1 Routing
const v1Router = express.Router();
v1Router.get('/tasks', (req, res) => {
res.json({ data: tasks, version: 'v1' }); // v1 Return Format
});
// v2 Routing(Response Format Upgrade)
const v2Router = express.Router();
v2Router.get('/tasks', (req, res) => {
res.json({ // v2 Return Format(Includes pagination)
data: tasks,
pagination: { page: 1, total: tasks.length }
});
});
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
// Response Header Version
app.use('/api/v2', (req, res, next) => {
res.setHeader('X-API-Version', '2.0');
next();
});
8. Modelo de Maturidade REST
(1) O Modelo de Maturidade de Richardson
Leonard Richardson propôs um modelo para medir a maturidade RESTful de uma API:
graph TD
L0["Level 0: Single endpoint<br/>HTTP as a tunnel<br/>e.g. POST /api {action: getTasks}"]
L1["Level 1: Resource URLs<br/>One URL per resource<br/>e.g. POST /tasks, POST /users"]
L2["Level 2: HTTP Methods<br/>GET/POST/PUT/DELETE<br/>e.g. GET /tasks, DELETE /tasks/1"]
L3["Level 3: HATEOAS<br/>Responses contain hyperlinks<br/>e.g. Response includes next, self links"]
L0 --> L1 --> L2 --> L3
style L0 fill:#ff6b6b,color:#fff
style L1 fill:#ffa502,color:#fff
style L2 fill:#2ed573,color:#fff
style L3 fill:#1e90ff,color:#fff
| Nível | Características | Exemplo de solicitação | Exemplo de resposta |
|---|---|---|---|
| Nível 0 | Túnel HTTP, URL única | POST /api {"action":"getTasks"} |
{"tasks": [...]} |
| Nível 1 | Separação de resíduos; qualquer método permitido | POST /tasks |
{"tasks": [...]} |
| Nível 2 | HTTP semanticamente correto | GET /tasks |
200 {"data": [...]} |
| Nível 3 | HATEOAS Hypermedia | GET /tasks/1 |
Inclui a navegação _links |
(2) Uma explicação detalhada sobre o HATEOAS
O HATEOAS (Hypermedia as the Engine of Application State) exige que as respostas incluam links para operações relevantes, de modo que os clientes não precisem codificar URLs de forma estática.
▶ Exemplo: Nível 3 — Resposta com um link HATEOAS
app.get('/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).json({ error: 'Not found' });
res.json({
...task,
_links: {
self: { href: `/tasks/${task.id}`, method: 'GET' },
update: { href: `/tasks/${task.id}`, method: 'PUT' },
delete: { href: `/tasks/${task.id}`, method: 'DELETE' },
assign: { href: `/tasks/${task.id}/assignee`, method: 'POST' },
comments: { href: `/tasks/${task.id}/comments`, method: 'GET' }
}
});
});
// Response
{
"id": "1",
"タイトル": "Learn REST",
"status": "pending",
"createdAt": "2025-07-03T10:30:00Z",
"_links": {
"self": { "href": "/tasks/1", "method": "GET" },
"update": { "href": "/tasks/1", "method": "PUT" },
"remove": { "href": "/tasks/1", "method": "DELETE" },
"assign": { "href": "/tasks/1/assignee", "method": "POST" },
"comments": { "href": "/tasks/1/comments", "method": "GET" }
}
}
9. Exemplo abrangente: projeto completo da API de gerenciamento de tarefas
A equipe de Alice desenvolveu uma API RESTful completa para o sistema de gerenciamento de tarefas, abrangendo tudo, desde as definições de recursos até o tratamento de erros.
▶ Exemplo: API de gerenciamento de tarefas completa
(1) Definição de recurso
| Recurso | Caminho | Descrição |
|---|---|---|
| Coleção de tarefas | /api/v1/tasks |
Todas as tarefas |
| Tarefa única | /api/v1/tasks/:id |
Tarefa especificada |
| Comentários sobre a tarefa | /api/v1/tasks/:id/comments |
Comentários sobre uma tarefa específica |
| Tag da tarefa | /api/v1/tasks/:id/tags |
Tag para uma tarefa específica |
(2) Mapeamento de métodos e solicitação/resposta
const express = require('express');
const app = express();
app.use(express.json());
let tasks = [
{ id: 1, title: 'Design database schema', status: 'done', priority: 'high', createdAt: '2025-07-01T08:00:00Z' },
{ id: 2, title: 'Implement REST API', status: 'in-progress', priority: 'high', createdAt: '2025-07-02T09:00:00Z' }
];
let nextId = 3;
// GET /api/v1/tasks — Get the task list
app.get('/api/v1/tasks', (req, res) => {
const { status, priority, page = 1, limit = 20 } = req.query;
let result = [...tasks];
if (status) result = result.filter(t => t.status === status);
if (priority) result = result.filter(t => t.priority === priority);
const start = (page - 1) * limit;
const data = result.slice(start, start + Number(limit));
res.json({
data,
pagination: {
page: Number(page),
limit: Number(limit),
total: result.length,
totalPages: Math.ceil(result.length / Number(limit))
}
});
});
// GET /api/v1/tasks/:id — Get a Single Task
app.get('/api/v1/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) {
return res.status(404).json({
error: 'Task not found',
details: [{ field: 'id', message: `No task with id ${req.params.id}` }],
timestamp: new Date().toISOString()
});
}
res.json({
data: task,
_links: {
self: { href: `/api/v1/tasks/${task.id}` },
update: { href: `/api/v1/tasks/${task.id}`, method: 'PUT' },
delete: { href: `/api/v1/tasks/${task.id}`, method: 'DELETE' },
comments: { href: `/api/v1/tasks/${task.id}/comments` }
}
});
});
// POST /api/v1/tasks — Create a Task
app.post('/api/v1/tasks', (req, res) => {
const { title, priority } = req.body;
if (!title) {
return res.status(400).json({
error: 'Validation failed',
details: [{ field: 'title', message: 'Title is required' }],
timestamp: new Date().toISOString()
});
}
const task = {
id: nextId++,
title,
status: 'pending',
priority: priority || 'medium',
createdAt: new Date().toISOString()
};
tasks.push(task);
res.status(201).location(`/api/v1/tasks/${task.id}`).json({ data: task });
});
// PUT /api/v1/tasks/:id — Full Update
app.put('/api/v1/tasks/:id', (req, res) => {
const idx = tasks.findIndex(t => t.id === parseInt(req.params.id));
if (idx === -1) {
return res.status(404).json({
error: 'Task not found',
details: [{ field: 'id', message: `No task with id ${req.params.id}` }],
timestamp: new Date().toISOString()
});
}
const { title, status, priority } = req.body;
if (!title || !status) {
return res.status(400).json({
error: 'Validation failed',
details: [
...(!title ? [{ field: 'title', message: 'Title is required' }] : []),
...(!status ? [{ field: 'status', message: 'Status is required' }] : [])
],
timestamp: new Date().toISOString()
});
}
tasks[idx] = { id: tasks[idx].id, title, status, priority: priority || 'medium', createdAt: tasks[idx].createdAt };
res.json({ data: tasks[idx] });
});
// PATCH /api/v1/tasks/:id — Partial Update
app.patch('/api/v1/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) {
return res.status(404).json({
error: 'Task not found',
details: [{ field: 'id', message: `No task with id ${req.params.id}` }],
timestamp: new Date().toISOString()
});
}
Object.assign(task, req.body);
res.json({ data: task });
});
// DELETE /api/v1/tasks/:id — Delete Task
app.delete('/api/v1/tasks/:id', (req, res) => {
const idx = tasks.findIndex(t => t.id === parseInt(req.params.id));
if (idx === -1) {
return res.status(404).json({
error: 'Task not found',
details: [{ field: 'id', message: `No task with id ${req.params.id}` }],
timestamp: new Date().toISOString()
});
}
tasks.splice(idx, 1);
res.status(204).end();
});
app.listen(3000, () => console.log('Task API running on port 3000'));
(3) Referência rápida para solicitações e respostas
# Create a Task
curl -X POST http://localhost:3000/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Write documentation","priority":"high"}'
# Get the task list(Filter + Pagination)
curl http://localhost:3000/api/v1/tasks?status=pending&page=1&limit=10
# Partial Update
curl -X PATCH http://localhost:3000/api/v1/tasks/1 \
-H "Content-Type: application/json" \
-d '{"status":"done"}'
# Delete Task
curl -X DELETE http://localhost:3000/api/v1/tasks/2
// POST Created successfully → 201
Status: 201 Created
Location: /api/v1/tasks/3
{ "data": { "id": 3, "title": "Write documentation", "status": "pending", "priority": "high", "createdAt": "2025-07-03T10:30:00Z" } }
// PATCH Update successful → 200
{ "data": { "id": 1, "title": "Design database schema", "status": "done", "priority": "high", "createdAt": "2025-07-01T08:00:00Z" } }
// DELETE Success → 204
Status: 204 No Content
(empty body)
// 404 Error
{ "error": "Task not found", "details": [{ "field": "id", "message": "No task with id 999" }], "timestamp": "2025-07-03T10:30:00Z" }
// 400 Validation Error
{ "error": "Validation failed", "details": [{ "field": "title", "message": "Title is required" }], "timestamp": "2025-07-03T10:30:00Z" }
❓ Perguntas Frequentes
P: Qual é a diferença entre REST e GraphQL? R: O REST é baseado em recursos; cada URL corresponde a um recurso, que é manipulado por meio de métodos HTTP. O GraphQL é baseado em uma linguagem de consulta; os clientes recuperam campos sob demanda a partir de um único endpoint. O REST é adequado para cenários CRUD com recursos bem definidos, enquanto o GraphQL é adequado para consultas relacionais complexas.
P: Qual é a diferença entre PUT e PATCH? R: O PUT realiza uma substituição completa; é necessário fornecer todos os campos do recurso, e quaisquer campos ausentes serão definidos com seus valores padrão. O PATCH realiza uma atualização parcial; ele modifica apenas os campos fornecidos, enquanto os campos não fornecidos permanecem inalterados. O PUT é idempotente, enquanto não há garantia de que o PATCH seja idempotente.
P: Qual é a melhor maneira de versionar uma API? R: O versionamento por caminho de URL (
/api/v1/) é o mais intuitivo; pode ser testado diretamente em um navegador e é a opção escolhida pela maioria das APIs públicas. O versionamento por cabeçalho de solicitação é mais RESTful, mas mais complicado de depurar. Os parâmetros de consulta são os mais simples, mas são facilmente ignorados. O versionamento por caminho de URL é recomendado para iniciantes.
P: Uma API REST precisa retornar JSON? R: Não necessariamente. O REST não restringe o formato; você pode usar XML, HTML, JSON e outros. No entanto, o JSON é atualmente o formato mais utilizado, pois é leve, fácil de analisar e compatível nativamente com JavaScript. Você pode negociar o formato usando o cabeçalho
Content-Type.
P: O que é idempotência? R: Idempotência significa que executar a mesma solicitação uma vez produz o mesmo resultado que executá-la várias vezes. GET é idempotente (várias leituras produzem o mesmo resultado), PUT é idempotente (várias substituições produzem o mesmo resultado), DELETE é idempotente (excluir um recurso que já foi excluído ainda retorna um sucesso) e POST não é idempotente (ele cria um novo recurso a cada vez).
P: O que devo fazer se a hierarquia de recursos for muito profunda? R: Se a hierarquia exceder dois níveis de aninhamento, considere promover os recursos filhos a recursos de nível superior e vinculá-los por meio de parâmetros de consulta. Por exemplo, se
/users/42/tasks/1/comments/5for muito profundo, você pode alterá-lo para/comments/5ou/tasks/1/comments/5.
P: Como uma API REST lida com operações em massa? R: Não existe uma abordagem padrão no REST. As práticas comuns incluem: usar uma solicitação POST
/tasks/batchcom um array para criar registros; usar uma solicitação PATCH/taskscom um array para atualizar registros em massa; e usar uma solicitação DELETE/tasks?ids=1,2,3para excluir registros em massa. Os endpoints personalizados devem ser claramente documentados.
📖 Resumo
- Conceitos-chave e como aplicá-los
- 1 Conceitos fundamentais e aplicação dos princípios da arquitetura REST
- 2 Conceitos fundamentais e uso de CRUD e mapeamento de métodos HTTP
- 3 Conceitos fundamentais e aplicação das diretrizes de design de URLs
- 4 Conceitos-chave e uso dos códigos de status HTTP
- 5 Conceitos fundamentais e uso dos formatos de solicitação e resposta
- 6 Conceitos fundamentais e uso das estratégias de versionamento de API
- 7 Conceitos fundamentais e aplicação do Modelo de Maturidade REST
📝 Exercícios
- Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
- Modifique o exemplo completo e adicione suas próprias extensões
- Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
- Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
- Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.