Guia de Início Rápido de API REST
APIs REST são a linguagem universal dos microsserviços—elas usam verbos HTTP para manipular recursos e JSON para transferir dados, tornando-as simples, padronizadas e eficientes.
1. O Que Você Vai Aprender
- Sistema de Anotações
@RestController/@RequestMapping/@GetMapping/@PostMapping - Variável de caminho
@PathVariable, parâmetro de requisição@RequestParam, corpo da requisição@RequestBody - Códigos de Status HTTP e Respostas Personalizadas com
ResponseEntity - Testando APIs REST com Postman / curl
- Alice implementa as quatro operações para pedidos: criação, consulta, atualização e exclusão
2. Uma História Real de um Desenvolvedor de API
(1) Ponto de Dor: Especificações de API Inconsistentes
Alice entrou em uma equipe de projeto existente e descobriu que o design da API REST era caótico: alguns métodos usavam GET para modificar dados, a nomenclatura de URLs era inconsistente (/getOrder, /order/list, /deleteOrderById), e os formatos de resposta não eram padronizados—alguns retornavam JSON, alguns retornavam XML, e respostas de erro eram texto puro. Bob, um desenvolvedor front-end, tinha que consultar a equipe back-end toda vez que integrava uma nova API, desperdiçando uma quantidade significativa de tempo em comunicação.
(2) A Abordagem RESTful
REST usa um conjunto de convenções uniformes para definir o design de API:
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping("/{id}") // GET /api/orders/123
@PostMapping // POST /api/orders
@PutMapping("/{id}") // PUT /api/orders/123
@DeleteMapping("/{id}") // DELETE /api/orders/123
}
(3) Resultado
Depois que Alice refatorou a API do OrderFlow para seguir o estilo RESTful—com convenções de nomenclatura de URL consistentes e semântica de verbos HTTP claramente definida—Bob, um desenvolvedor front-end, remarked, "Você pode saber como usá-la apenas olhando a URL." Como resultado, o tempo necessário para integração com a API foi reduzido de uma média de 2 horas para 15 minutos.
3. Conceitos Principais do REST
(1) Os Seis Princípios do REST
REST (Representational State Transfer) define as restrições centrais deste estilo arquitetural:
graph TD
A[REST Constraints] --> B[Client-Server<br/>Separation of Concerns]
A --> C[Stateless<br/>Each Request Contains<br/>All Needed Info]
A --> D[Cacheable<br/>Responses Define<br/>Cacheability]
A --> E[Uniform Interface<br/>Consistent API Design]
A --> F[Layered System<br/>Client Cannot Tell<br/>If Connected Directly]
A --> G[Code on Demand<br/>Optional: Server Can<br/>Send Executable Code]
(2) Mapeando Verbos HTTP para Operações CRUD
| Verbo HTTP | Operação | Idempotência | Segurança | URL Típica |
|---|---|---|---|---|
| GET | Consulta | Sim | Sim | /api/orders |
| POST | Criar | Não | Não | /api/orders |
| PUT | Atualização Completa | Sim | Não | /api/orders/123 |
| PATCH | Atualização Parcial | Não | Não | /api/orders/123 |
| DELETE | Excluir | Sim | Não | /api/orders/123 |
4. @RestController e Mapeamento de Requisição
(1) Como o @RestController Funciona
@RestController = @Controller + @ResponseBody indica que os valores de retorno de todos os métodos nesta classe são escritos diretamente no corpo da resposta HTTP (JSON por padrão).
(1) ▶ Exemplo: OrderController Básico
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final List<Map<String, Object>> orders = new ArrayList<>();
@GetMapping
public List<Map<String, Object>> listOrders() {
return orders;
}
@PostMapping
public Map<String, Object> createOrder(
@RequestBody Map<String, Object> order) {
order.put("id", (long) (orders.size() + 1));
order.put("status", "PENDING");
orders.add(order);
return order;
}
}
Saída:
// Execução bem-sucedida
(2) Binding de Parâmetros de Requisição
(2) ▶ Exemplo: @PathVariable e @RequestParam
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping("/{id}")
public Map<String, Object> getOrder(@PathVariable Long id) {
return Map.of("id", id, "status", "PENDING");
}
@GetMapping
public List<Map<String, Object>> searchOrders(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String status) {
return List.of(Map.of(
"page", page, "size", size, "status", status
));
}
}
Saída:
// Execução bem-sucedida
| Anotação | Finalidade | Exemplo | URL |
|---|---|---|---|
@PathVariable |
Extrair variáveis do caminho | @PathVariable Long id |
/api/orders/123 |
@RequestParam |
Extrair Parâmetros de Consulta | @RequestParam String status |
/api/orders?status=PENDING |
@RequestBody |
Extrair JSON do corpo da requisição | @RequestBody OrderRequest req |
Corpo do POST |
@RequestHeader |
Extrair Headers da Requisição | @RequestHeader String auth |
Header: Authorization |
(3) ▶ Exemplo: Usando um DTO para Receber o Corpo da Requisição
public record CreateOrderRequest(
Long productId,
Integer quantity,
String shippingAddress
) {}
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@PostMapping
public ResponseEntity<Map<String, Object>> createOrder(
@RequestBody CreateOrderRequest request) {
Map<String, Object> order = Map.of(
"productId", request.productId(),
"quantity", request.quantity(),
"shippingAddress", request.shippingAddress(),
"status", "PENDING"
);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(order);
}
}
Saída:
// Execução bem-sucedida
5. ResponseEntity e Códigos de Status HTTP
(1) Usando ResponseEntity
ResponseEntity Dá a você controle total sobre as respostas HTTP: códigos de status, headers de resposta e corpo da resposta.
| Método | Cenários Aplicáveis | Flexibilidade |
|---|---|---|
| Retorna um objeto diretamente | Resposta de sucesso simples | Baixa (fixo em 200) |
ResponseEntity.ok(body) |
Deve ser definido como 200 | Média |
ResponseEntity.status(CREATED).body(body) |
Criação de recurso retornou 201 | Alta |
ResponseEntity.notFound().build() |
Recurso não encontrado—retorna 404 | Alta |
(1) ▶ Exemplo: Operações CRUD Completas
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final Map<Long, Map<String, Object>> orderStore = new ConcurrentHashMap<>();
private final AtomicLong idGenerator = new AtomicLong(1);
@PostMapping
public ResponseEntity<Map<String, Object>> create(
@RequestBody CreateOrderRequest request) {
Long id = idGenerator.getAndIncrement();
Map<String, Object> order = new HashMap<>();
order.put("id", id);
order.put("productId", request.productId());
order.put("quantity", request.quantity());
order.put("status", "PENDING");
orderStore.put(id, order);
return ResponseEntity.status(HttpStatus.CREATED).body(order);
}
@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> getOne(@PathVariable Long id) {
Map<String, Object> order = orderStore.get(id);
if (order == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(order);
}
@PutMapping("/{id}")
public ResponseEntity<Map<String, Object>> update(
@PathVariable Long id,
@RequestBody CreateOrderRequest request) {
if (!orderStore.containsKey(id)) {
return ResponseEntity.notFound().build();
}
Map<String, Object> order = new HashMap<>();
order.put("id", id);
order.put("productId", request.productId());
order.put("quantity", request.quantity());
order.put("status", "CONFIRMED");
orderStore.put(id, order);
return ResponseEntity.ok(order);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
if (orderStore.remove(id) == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
}
Saída:
// Execução bem-sucedida
6. Métodos de Teste de API
(1) Comando de teste curl
(1) ▶ Exemplo: Testando APIs CRUD com curl
# Criar pedido
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{"productId":1, "quantity":3, "shippingAddress":"123 Main St"}'
# Obter pedido
curl http://localhost:8080/api/orders/1
# Listar pedidos com paginação
curl "http://localhost:8080/api/orders?page=0&size=10"
# Atualizar pedido
curl -X PUT http://localhost:8080/api/orders/1 \
-H "Content-Type: application/json" \
-d '{"productId":2, "quantity":5, "shippingAddress":"456 Oak Ave"}'
# Excluir pedido
curl -X DELETE http://localhost:8080/api/orders/1
Saída:
{"status":"ok","data":{}}
| Código de Status HTTP | Significado | Quando Retornado |
|---|---|---|
| 200 OK | Sucesso | Sucesso GET / PUT |
| 201 Created | Criado | Recurso criado com sucesso via POST |
| 204 No Content | Sem conteúdo | DELETE bem-sucedido |
| 400 Bad Request | Erro de Requisição | Falha na Validação de Parâmetros |
| 404 Not Found | Não Encontrado | Recurso Não Existe |
7. Especificações de Design de API RESTful
(1) Convenções de Nomenclatura de URL
| Regra | Forma Incorreta | Forma Correta |
|---|---|---|
| Uso de Substantivos no Plural | /getOrder |
/api/orders |
| Relações de Recursos Aninhados | /orderItems?orderId=1 |
/api/orders/1/items |
| Use Caminhos em Vez de Consultas | /api/orders?id=1 |
/api/orders/1 |
| Versionamento | Nenhum | /api/v1/orders |
| Parâmetros de Consulta para Filtragem | /api/pendingOrders |
/api/orders?status=PENDING |
8. Exemplo Abrangente: API CRUD de Pedidos do OrderFlow
package com.orderflow.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
public record CreateOrderRequest(
Long productId, Integer quantity, String shippingAddress
) {}
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
private final Map<Long, Map<String, Object>> store = new ConcurrentHashMap<>();
private final AtomicLong seq = new AtomicLong(1);
@PostMapping
public ResponseEntity<Map<String, Object>> create(
@RequestBody CreateOrderRequest req) {
Long id = seq.getAndIncrement();
Map<String, Object> order = new LinkedHashMap<>();
order.put("id", id);
order.put("productId", req.productId());
order.put("quantity", req.quantity());
order.put("shippingAddress", req.shippingAddress());
order.put("status", "PENDING");
order.put("createdAt", java.time.Instant.now());
store.put(id, order);
return ResponseEntity.status(HttpStatus.CREATED).body(order);
}
@GetMapping
public List<Map<String, Object>> list(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return store.values().stream()
.skip((long) page * size).limit(size).toList();
}
@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> get(@PathVariable Long id) {
Map<String, Object> order = store.get(id);
return order != null ? ResponseEntity.ok(order)
: ResponseEntity.notFound().build();
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
return store.remove(id) != null
? ResponseEntity.noContent().build()
: ResponseEntity.notFound().build();
}
}
❓ Perguntas Frequentes
ResponseEntity ou apenas retornar o objeto diretamente?ResponseEntity quando precisar definir códigos de status personalizados (como 201 ou 404) ou headers de resposta.application.yml ou usar @JsonFormat(pattern = "yyyy-MM-dd") no campo./api/v1/orders), que é simples e intuitivo. Você também pode usar versionamento baseado em header (Accept: application/vnd.orderflow.v1+json), que é mais RESTful mas mais complexo de implementar.📖 Resumo
- REST mapeia operações CRUD para verbos HTTP: GET para recuperação, POST para criação, PUT para atualização e DELETE para exclusão
@RestController=@Controller+@ResponseBody, retorna JSON@PathVariableobtém a variável de caminho,@RequestParamobtém os parâmetros de consulta, e@RequestBodyobtém o corpo da requisiçãoResponseEntityControle total sobre códigos de status de resposta, headers e corpo- URLs RESTful usam substantivos no plural; aninhamento indica relações entre recursos; parâmetros de consulta são usados para filtragem
📝 Exercícios
-
Exercício Básico (Dificuldade ⭐): Implemente APIs CRUD para o recurso Product no OrderFlow, incluindo
GET /api/v1/products,POST /api/v1/products,GET /api/v1/products/{id}eDELETE /api/v1/products/{id}. -
Problema Avançado (Dificuldade: ⭐⭐): Adicione o endpoint
PATCH /api/v1/orders/{id}/statusà API de Pedidos que atualiza apenas o campo de status do pedido. Considere a diferença entre PATCH e PUT. -
Desafio (Dificuldade: ⭐⭐⭐): Implemente a interface de recurso aninhado
GET /api/v1/orders/{id}/itemspara retornar todos os itens de pedido sob um pedido especificado, com suporte para parâmetros de paginação.



