JAVA Learning

Hub de Conteúdo

🔌 APIs - Interface de Programação de Aplicações

Conceitos, Arquitetura, Protocolos e Integração

1. O que é uma API?

API (Application Programming Interface) é um conjunto de regras que permite que diferentes sistemas se comuniquem e troquem informações de forma padronizada. Ela define como solicitações devem ser feitas, quais dados podem ser enviados e como as respostas serão estruturadas.

Ao usar uma API, o cliente faz uma requisição especificando o que deseja acessar. Essa requisição é enviada a um servidor, que processa o pedido e devolve uma resposta estruturada, normalmente no formato JSON ou XML.

Para acessar diferentes recursos dentro de um sistema, utilizam-se endpoints, que são como endereços específicos para cada funcionalidade. Por exemplo: /api/usuarios pode retornar uma lista de usuários.

As operações mais comuns em uma API seguem o padrão HTTP:

  • GET: obter dados
  • POST: enviar dados e criar novos registros
  • PUT/PATCH: atualizar informações existentes
  • DELETE: excluir dados

Muitas APIs exigem autenticação para garantir segurança. Isso pode ser feito com tokens, chaves de acesso ou protocolos como OAuth, protegendo informações e evitando acessos não autorizados.

APIs também podem ter limites de requisição (rate limits) para evitar sobrecarga, garantindo estabilidade e desempenho do sistema.

No mundo real, APIs permitem que aplicativos usem mapas, façam pagamentos, se conectem a redes sociais, enviem mensagens, consultem bancos de dados e muito mais, tornando o desenvolvimento mais rápido e eficiente.

Em resumo, uma API é a ponte que conecta sistemas, permitindo inovação sem precisar reinventar funcionalidades já existentes.

2. Conceitos Fundamentais

Cliente (Client)

  • Função: Solicita dados ou funcionalidades
  • Exemplos: Website, app mobile, outro backend
  • Ação: Envia requisições HTTP
  • Responsabilidade: Formatar dados corretamente

Servidor (Server)

  • Função: Fornece dados ou funcionalidades
  • Exemplos: Backend em Java, Node.js, Python
  • Ação: Processa requisições e retorna respostas
  • Responsabilidade: Validar e processar dados

3. Tipos de APIs

Existem diferentes tipos de APIs, cada uma com suas características, vantagens e casos de uso específicos.

REST API (Representational State Transfer):

http
// Mais popular e recomendada
// Usa URLs para representar recursos
// Métodos HTTP para operações (GET, POST, PUT, DELETE)

GET /api/usuarios           // Listar todos
GET /api/usuarios/123       // Obter um específico
POST /api/usuarios          // Criar novo
PUT /api/usuarios/123       // Atualizar
DELETE /api/usuarios/123    // Deletar

// Características:
// ✓ Simples e intuitiva
// ✓ Escalável
// ✓ Stateless (sem estado)
// ✓ Cacheable

SOAP (Simple Object Access Protocol):

xml
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/">
  <soap:Body>
    <GetUsuario>
      <id>123</id>
    </GetUsuario>
  </soap:Body>
</soap:Envelope>

// Características:
// ✓ Muito seguro
// ✓ Padrão W3C
// ✗ Pesado e complexo
// ✗ Menos usado atualmente (legado)

Webhook:

java
// A aplicação envia dados automaticamente quando algo acontece
// Orientada a eventos
// O servidor inicia a comunicação (não o cliente)

// Exemplo: Pagamento confirmado
POST https://seu-app.com/webhooks/pagamento
Content-Type: application/json

{
  "evento": "pagamento_confirmado",
  "id_pagamento": "pay_123",
  "valor": 150.00,
  "timestamp": "2025-10-29T14:30:00Z"
}

// Características:
// ✓ Orientado a eventos
// ✓ Tempo real
// ✓ Reduz polling
// ✗ Requer tratamento de erros robusto

4. Protocolo HTTP e Métodos

A maioria das APIs modernas usa HTTP como protocolo de transporte. Os principais métodos são:

Métodos HTTP e Operações:

http
// GET - Recuperar dados (READ)
GET /api/usuarios
GET /api/usuarios/123
Características: Seguro, Idempotente, Cacheável

// POST - Criar novo recurso (CREATE)
POST /api/usuarios
Body: { "nome": "João", "email": "joao@mail.com" }
Características: Não-idempotente, Pode ter efeitos colaterais

// PUT - Atualizar completamente (UPDATE)
PUT /api/usuarios/123
Body: { "nome": "João Silva", "email": "joao.silva@mail.com" }
Características: Idempotente, Substitui tudo

// PATCH - Atualizar parcialmente (PARTIAL UPDATE)
PATCH /api/usuarios/123
Body: { "nome": "João Silva" }
Características: Idempotente, Atualiza apenas campos enviados

// DELETE - Remover recurso (DELETE)
DELETE /api/usuarios/123
Características: Idempotente, Remove permanentemente

// HEAD - Como GET mas sem body
HEAD /api/usuarios
Características: Verifica disponibilidade sem dados

// OPTIONS - Descreve opções de comunicação
OPTIONS /api/usuarios
Características: Usado para CORS pré-flight

5. Códigos de Status HTTP

Status Codes - Categorizados:

http
// 2xx - SUCESSO ✓
200 OK              - Requisição bem-sucedida
201 Created         - Recurso criado com sucesso
204 No Content      - Sucesso mas sem conteúdo no corpo
202 Accepted        - Requisição aceita mas ainda processando

// 3xx - REDIRECIONAMENTO ↻
301 Moved Permanently    - Mudou permanentemente
302 Found                - Mudou temporariamente
304 Not Modified         - Dados não alterados (use cache)
307 Temporary Redirect   - Redireção temporária

// 4xx - ERRO DO CLIENTE ✗
400 Bad Request          - Requisição inválida
401 Unauthorized         - Autenticação necessária
403 Forbidden            - Autenticado mas sem permissão
404 Not Found            - Recurso não encontrado
409 Conflict             - Conflito (ex: email duplicado)
429 Too Many Requests    - Rate limit excedido

// 5xx - ERRO DO SERVIDOR ⚠
500 Internal Server Error    - Erro interno no servidor
503 Service Unavailable      - Serviço temporariamente indisponível
504 Gateway Timeout          - Timeout na comunicação

6. Exemplo Prático: REST API com Spring Boot

Vamos criar uma REST API completa de gerenciamento de usuários em Java com Spring Boot.

Modelo (Entidade):

java
import javax.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "usuarios")
public class Usuario {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String nome;
    
    @Column(nullable = false, unique = true)
    private String email;
    
    @Column(nullable = false)
    private String senha;
    
    private boolean ativo = true;
    
    @Column(name = "criado_em", updatable = false)
    private LocalDateTime criadoEm = LocalDateTime.now();
    
    @Column(name = "atualizado_em")
    private LocalDateTime atualizadoEm;
}

Repositório (Data Access):

java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UsuarioRepository extends JpaRepository {
    Usuario findByEmail(String email);
    
    boolean existsByEmail(String email);
}

Controlador (REST Endpoints):

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.time.LocalDateTime;

@RestController
@RequestMapping("/api/usuarios")
@CrossOrigin(origins = "*")
public class UsuarioController {
    
    @Autowired
    private UsuarioRepository usuarioRepository;
    
    // GET - Listar todos
    @GetMapping
    public ResponseEntity> listarTodos() {
        List usuarios = usuarioRepository.findAll();
        return ResponseEntity.ok(usuarios);
    }
    
    // GET - Buscar por ID
    @GetMapping("/{id}")
    public ResponseEntity buscarPorId(@PathVariable Long id) {
        return usuarioRepository.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }
    
    // POST - Criar novo usuário
    @PostMapping
    public ResponseEntity criar(@RequestBody Usuario usuario) {
        if (usuarioRepository.existsByEmail(usuario.getEmail())) {
            return ResponseEntity.status(HttpStatus.CONFLICT)
                .body("Email já registrado");
        }
        usuario.setCriadoEm(LocalDateTime.now());
        usuario.setAtualizado(LocalDateTime.now());
        Usuario salvo = usuarioRepository.save(usuario);
        return ResponseEntity.status(HttpStatus.CREATED).body(salvo);
    }
    
    // PUT - Atualizar completamente
    @PutMapping("/{id}")
    public ResponseEntity atualizar(
        @PathVariable Long id, 
        @RequestBody Usuario usuarioAtualizado) {
        
        return usuarioRepository.findById(id)
            .map(usuario -> {
                usuario.setNome(usuarioAtualizado.getNome());
                usuario.setEmail(usuarioAtualizado.getEmail());
                usuario.setAtivo(usuarioAtualizado.isAtivo());
                usuario.setAtualizadoEm(LocalDateTime.now());
                Usuario salvo = usuarioRepository.save(usuario);
                return ResponseEntity.ok(salvo);
            })
            .orElse(ResponseEntity.notFound().build());
    }
    
    // PATCH - Atualizar parcialmente
    @PatchMapping("/{id}")
    public ResponseEntity atualizarParcial(
        @PathVariable Long id,
        @RequestBody Usuario usuarioAtualizado) {
        
        return usuarioRepository.findById(id)
            .map(usuario -> {
                if (usuarioAtualizado.getNome() != null) {
                    usuario.setNome(usuarioAtualizado.getNome());
                }
                if (usuarioAtualizado.getEmail() != null) {
                    usuario.setEmail(usuarioAtualizado.getEmail());
                }
                usuario.setAtualizadoEm(LocalDateTime.now());
                Usuario salvo = usuarioRepository.save(usuario);
                return ResponseEntity.ok(salvo);
            })
            .orElse(ResponseEntity.notFound().build());
    }
    
    // DELETE - Remover usuário
    @DeleteMapping("/{id}")
    public ResponseEntity deletar(@PathVariable Long id) {
        if (usuarioRepository.existsById(id)) {
            usuarioRepository.deleteById(id);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }
}

Exemplos de Requisições:

http
// 1️⃣ GET - Listar todos os usuários
GET http://localhost:8080/api/usuarios
Accept: application/json

Resposta 200 OK:
[
  {
    "id": 1,
    "nome": "Ana Silva",
    "email": "ana@example.com",
    "ativo": true,
    "criadoEm": "2025-10-29T10:30:00"
  },
  {
    "id": 2,
    "nome": "Bruno Costa",
    "email": "bruno@example.com",
    "ativo": true,
    "criadoEm": "2025-10-29T11:15:00"
  }
]

---

// 2️⃣ GET - Buscar usuário específico
GET http://localhost:8080/api/usuarios/1
Accept: application/json

Resposta 200 OK:
{
  "id": 1,
  "nome": "Ana Silva",
  "email": "ana@example.com",
  "ativo": true,
  "criadoEm": "2025-10-29T10:30:00"
}

---

// 3️⃣ POST - Criar novo usuário
POST http://localhost:8080/api/usuarios
Content-Type: application/json

{
  "nome": "Carlos Souza",
  "email": "carlos@example.com",
  "senha": "senha123"
}

Resposta 201 Created:
{
  "id": 3,
  "nome": "Carlos Souza",
  "email": "carlos@example.com",
  "ativo": true,
  "criadoEm": "2025-10-29T14:45:00"
}

---

// 4️⃣ PUT - Atualizar usuário completamente
PUT http://localhost:8080/api/usuarios/1
Content-Type: application/json

{
  "nome": "Ana Silva Santos",
  "email": "ana.silva@example.com",
  "ativo": true
}

Resposta 200 OK: (usuário atualizado)

---

// 5️⃣ PATCH - Atualizar apenas o nome
PATCH http://localhost:8080/api/usuarios/1
Content-Type: application/json

{
  "nome": "Ana S. Santos"
}

Resposta 200 OK: (apenas nome atualizado)

---

// 6️⃣ DELETE - Remover usuário
DELETE http://localhost:8080/api/usuarios/1

Resposta 204 No Content

7. Autenticação e Segurança

APIs precisam proteger dados sensíveis. Existem várias estratégias de autenticação:

Basic Auth:

http
// Envia usuário e senha em cada requisição (Base64)
GET /api/usuarios HTTP/1.1
Authorization: Basic dXN1YXJpbzpzZW5oYQ==

// dXN1YXJpbzpzZW5oYQ== é base64 de "usuario:senha"
// ✓ Simples
// ✗ Inseguro sem HTTPS
// ✗ Credenciais em toda requisição

Bearer Token:

http
// Cliente obtém token após autenticação
// Envia token em cada requisição

POST /api/auth/login
Content-Type: application/json

{
  "email": "usuario@example.com",
  "senha": "senha123"
}

Resposta 200 OK:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "tipo": "Bearer",
  "expiraEm": "2025-10-30T14:30:00"
}

---

// Usar token em requisições subsequentes
GET /api/usuarios
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

JWT (JSON Web Token):

java
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.util.Date;

public class JwtUtil {
    private static final String SECRET_KEY = "sua_chave_super_secreta_aqui_com_minimo_32_caracteres";
    private static final long EXPIRATION_TIME = 86400000; // 24 horas
    
    // Gerar token
    public static String gerarToken(String email, Long usuarioId) {
        return Jwts.builder()
            .setSubject(email)
            .claim("usuarioId", usuarioId)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
            .signWith(
                Keys.hmacShaKeyFor(SECRET_KEY.getBytes()),
                SignatureAlgorithm.HS256
            )
            .compact();
    }
    
    // Validar e extrair email do token
    public static String extrairEmail(String token) {
        return Jwts.parserBuilder()
            .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
            .build()
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
    
    // Validar se token é válido
    public static boolean validarToken(String token) {
        try {
            Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
                .build()
                .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

8. Boas Práticas para APIs

✓ Versionamento de API

  • Use versões na URL: /api/v1/usuarios
  • Permite evoluir sem quebrar clientes antigos
  • Facilita migração gradual

✓ Nomes Descritivos e Consistentes

  • Use substantivos para endpoints: /usuarios, /produtos
  • Evite verbos: /obterUsuarios ❌ → /usuarios ✓
  • Use plural: /produtos não /produto

✓ Tratamento de Erros

  • Retorne mensagens claras e úteis
  • Use status codes apropriados
  • Padronize o formato de erro em JSON

✓ Documentação

  • Use Swagger/OpenAPI para gerar documentação automática
  • Documente todos os endpoints
  • Inclua exemplos de requisição e resposta

9. Exercícios Práticos

Exercício 1: API de Tarefas

Crie uma REST API com CRUD completo (Create, Read, Update, Delete) para gerenciar tarefas. Use Spring Boot, JPA e MySQL.

Exercício 2: Integração com API Externa

Use RestTemplate ou WebClient para integrar sua aplicação com uma API pública (ex: ViaCEP para endereços). Trate erros corretamente.

Exercício 3: Autenticação com JWT

Implemente autenticação JWT em uma API existente. Crie endpoints de login e proteja outros endpoints verificando o token.

Exercício 4: Documentação com Swagger

Configure Swagger/SpringFox em um projeto Spring Boot. Documente todos os endpoints com descrições, parâmetros e exemplos.

Exercício 5: Tratamento Centralizado de Erros

Crie um @ControllerAdvice que padroniza respostas de erro em JSON com mensagens claras e status codes apropriados.

Conclusão

APIs são a espinha dorsal da web moderna e comunicação entre sistemas. Entender seus conceitos é essencial para qualquer desenvolvedor.

Pontos-chave para lembrar:

  • REST é o padrão mais popular para APIs web
  • HTTP métodos definem as operações (GET, POST, PUT, DELETE, PATCH)
  • Status codes comunicam o resultado da operação
  • Autenticação é essencial para proteger dados
  • Documentação e boas práticas tornam a API usável e mantível

Pratique criando suas próprias APIs e integrando-se com APIs de terceiros. Isso é um dos conhecimentos mais valiosos no desenvolvimento moderno! 🚀