404 Not Found

404 Not Found


nginx

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


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:

JAVA
@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:

100%
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
📌 Ponto-Chave: Idempotência significa que executar uma operação múltiplas vezes produz o mesmo resultado. PUT é idempotente (substitui o recurso inteiro), enquanto POST não é idempotente (pode criar um novo recurso a cada vez).


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

JAVA
@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:

TEXT
// Execução bem-sucedida

(2) Binding de Parâmetros de Requisição

(2) ▶ Exemplo: @PathVariable e @RequestParam

JAVA
@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:

TEXT
// 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

JAVA
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:

TEXT
// 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

JAVA
@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:

TEXT
// Execução bem-sucedida

6. Métodos de Teste de API

(1) Comando de teste curl

(1) ▶ Exemplo: Testando APIs CRUD com curl

BASH
# 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:

TEXT
{"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

JAVA
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

P Qual é a diferença entre @Controller e @RestController?
R @Controller retorna um nome de view (para uso com engine de template), enquanto @RestController = @Controller + @ResponseBody, que serializa o valor de retorno diretamente para JSON. Sempre use @RestController ao escrever APIs REST.
P Qual é a diferença entre PUT e PATCH?
R PUT faz uma substituição completa, então você deve passar o objeto inteiro; PATCH faz uma atualização parcial, então você só precisa passar os campos que precisam ser modificados. Spring Boot suporta ambos, mas PATCH requer lógica de merge personalizada.
P Quando devo usar @PathVariable e @RequestParam?
R @PathVariable é usado para identificar o identificador único de um recurso (como o ID de um pedido), enquanto @RequestParam é usado para parâmetros de filtragem e paginação (como status e page). Regra simples: Use @PathVariable para caminhos e @RequestParam para parâmetros de consulta.
P Devo retornar um ResponseEntity ou apenas retornar o objeto diretamente?
R Para consultas simples, você pode simplesmente retornar o objeto (que retorna automaticamente um código de status 200). Use ResponseEntity quando precisar definir códigos de status personalizados (como 201 ou 404) ou headers de resposta.
P Como os formatos de data e hora devem ser tratados?
R Por padrão, eles são serializados como timestamps. Você pode configurar isso em application.yml ou usar @JsonFormat(pattern = "yyyy-MM-dd") no campo.
P Uma API REST requer versionamento?
R Recomendamos usar versionamento baseado em URL (/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


📝 Exercícios

  1. 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} e DELETE /api/v1/products/{id}.

  2. 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.

  3. Desafio (Dificuldade: ⭐⭐⭐): Implemente a interface de recurso aninhado GET /api/v1/orders/{id}/items para retornar todos os itens de pedido sob um pedido especificado, com suporte para parâmetros de paginação.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%