Voltar ao Glossario
Glossario Python

API REST: O que É e Como Funciona | Python Brasil

Guia completo sobre API REST: princípios REST, HATEOAS, Richardson Maturity Model, autenticação JWT, paginação, versionamento e OpenAPI.

O que é uma API REST?

Uma API REST (Representational State Transfer) é um estilo de arquitetura para comunicação entre sistemas via protocolo HTTP. O termo foi definido por Roy Fielding em sua tese de doutorado em 2000, descrevendo um conjunto de restrições que, quando aplicadas, resultam em sistemas escaláveis, simples e interoperáveis.

APIs REST são o padrão mais usado para comunicação entre frontend e backend, aplicações mobile, microsserviços e integrações entre sistemas.

Os 6 Princípios REST

1. Cliente-Servidor

A interface de usuário é separada do armazenamento de dados. O cliente não precisa conhecer os detalhes do servidor, e o servidor não conhece o estado da interface do cliente.

2. Stateless (Sem Estado)

Cada requisição deve conter todas as informações necessárias para ser processada. O servidor não guarda estado do cliente entre requisições. Isso facilita escalonamento horizontal, pois qualquer servidor pode atender qualquer requisição.

3. Cacheable (Cacheável)

Respostas devem indicar se podem ser armazenadas em cache. Isso reduz carga no servidor e melhora a performance do cliente.

4. Interface Uniforme

Quatro sub-restrições: identificação de recursos via URI, manipulação via representações, mensagens autodescritivas e HATEOAS.

5. Sistema em Camadas

O cliente não precisa saber se está falando diretamente com o servidor ou com um intermediário (proxy, load balancer, CDN).

6. Code-On-Demand (Opcional)

Servidores podem enviar código executável ao cliente (como JavaScript), mas isso é opcional e raramente aplicado em APIs REST modernas.

Richardson Maturity Model

O modelo de maturidade de Richardson classifica APIs REST em 4 níveis:

  • Nível 0: Uso do HTTP apenas como transporte (RPC via POST)
  • Nível 1: Recursos (URIs diferentes para cada entidade)
  • Nível 2: Métodos HTTP corretos (GET, POST, PUT, DELETE) e status codes
  • Nível 3: HATEOAS — respostas incluem links para ações possíveis

A maioria das “APIs REST” na prática atinge o nível 2. O nível 3 é o REST “maduro” segundo Fielding, mas é raramente implementado.

HATEOAS

HATEOAS (Hypermedia as the Engine of Application State) significa que a resposta da API inclui links para as próximas ações possíveis:

{
  "id": 1,
  "titulo": "Aprendendo Python",
  "status": "ativo",
  "_links": {
    "self": { "href": "/artigos/1" },
    "editar": { "href": "/artigos/1", "method": "PUT" },
    "deletar": { "href": "/artigos/1", "method": "DELETE" },
    "comentarios": { "href": "/artigos/1/comentarios" }
  }
}

Métodos HTTP e Status Codes

Métodos HTTP

  • GET: busca dados, sem efeitos colaterais (idempotente)
  • POST: cria um novo recurso
  • PUT: substitui um recurso completo (idempotente)
  • PATCH: atualiza parcialmente um recurso
  • DELETE: remove um recurso (idempotente)

Status Codes Mais Comuns

  • 200 OK: sucesso genérico
  • 201 Created: recurso criado com sucesso
  • 204 No Content: sucesso sem corpo de resposta (ex: DELETE)
  • 400 Bad Request: dados inválidos enviados pelo cliente
  • 401 Unauthorized: não autenticado
  • 403 Forbidden: autenticado, mas sem permissão
  • 404 Not Found: recurso não encontrado
  • 409 Conflict: conflito (ex: e-mail já cadastrado)
  • 422 Unprocessable Entity: dados sintaticamente corretos, mas semanticamente inválidos
  • 429 Too Many Requests: rate limit atingido
  • 500 Internal Server Error: erro no servidor

Exemplo Completo com FastAPI

from fastapi import FastAPI, HTTPException, Query, Header
from pydantic import BaseModel
from typing import Optional
from datetime import datetime

app = FastAPI(title="API de Artigos", version="1.0.0")

class ArtigoCreate(BaseModel):
    titulo: str
    conteudo: str
    categoria: str

class ArtigoResponse(BaseModel):
    id: int
    titulo: str
    categoria: str
    criado_em: datetime
    visualizacoes: int

class RespostaPaginada(BaseModel):
    dados: list[ArtigoResponse]
    total: int
    pagina: int
    tamanho: int

@app.get("/api/v1/artigos", response_model=RespostaPaginada)
async def listar_artigos(
    pagina: int = Query(default=1, ge=1, description="Número da página"),
    tamanho: int = Query(default=20, ge=1, le=100, description="Itens por página"),
    categoria: Optional[str] = Query(default=None),
    busca: Optional[str] = Query(default=None),
):
    # Simulação de paginação
    artigos = buscar_artigos(pagina, tamanho, categoria, busca)
    return RespostaPaginada(
        dados=artigos,
        total=200,
        pagina=pagina,
        tamanho=tamanho
    )

@app.post("/api/v1/artigos", response_model=ArtigoResponse, status_code=201)
async def criar_artigo(artigo: ArtigoCreate):
    novo = salvar_artigo(artigo)
    return novo

Autenticação

API Key

from fastapi import Security, HTTPException, status
from fastapi.security import APIKeyHeader

api_key_header = APIKeyHeader(name="X-API-Key")

async def verificar_api_key(api_key: str = Security(api_key_header)):
    if api_key != CHAVE_VALIDA:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Chave de API inválida"
        )
    return api_key

JWT (JSON Web Token)

from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")

async def usuario_atual(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        email = payload.get("sub")
        if not email:
            raise HTTPException(status_code=401, detail="Token inválido")
        return email
    except JWTError:
        raise HTTPException(status_code=401, detail="Token inválido ou expirado")

Paginação

Existem três estratégias principais de paginação:

Baseada em página (offset): simples, mas pode ter resultados inconsistentes em dados que mudam rapidamente.

GET /artigos?pagina=2&tamanho=20

Baseada em cursor: mais eficiente para grandes volumes de dados.

GET /artigos?cursor=eyJpZCI6MTAwfQ&tamanho=20

Baseada em keyset: usa o valor do último item para buscar o próximo lote.

GET /artigos?ultimo_id=100&tamanho=20

Versionamento

Estratégias de versionamento de APIs:

URI: mais comum e explícito.

GET /api/v1/artigos
GET /api/v2/artigos

Header: mais “puro” em termos REST, mas menos visível.

Accept: application/vnd.minhapi.v2+json

Query param: simples, mas mistura versão com dados.

GET /artigos?version=2

Rate Limiting

Rate limiting protege a API de abusos. A resposta deve incluir headers informativos:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1710000000
Retry-After: 60

Implementação simples com Redis (usando slowapi):

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

@app.get("/artigos")
@limiter.limit("100/minute")
async def listar_artigos(request: Request):
    return {"artigos": []}

OpenAPI e Swagger

O OpenAPI (antigo Swagger) é a especificação padrão para documentar APIs REST. O FastAPI gera o schema OpenAPI automaticamente:

  • Acesse /docs para a interface Swagger UI interativa
  • Acesse /redoc para a documentação ReDoc
  • Acesse /openapi.json para o schema em JSON

Você pode enriquecer a documentação:

@app.get(
    "/artigos/{artigo_id}",
    response_model=ArtigoResponse,
    summary="Buscar artigo por ID",
    description="Retorna os detalhes completos de um artigo.",
    responses={
        404: {"description": "Artigo não encontrado"},
        200: {"description": "Artigo encontrado com sucesso"}
    }
)
async def buscar_artigo(artigo_id: int):
    ...

REST vs GraphQL

AspectoRESTGraphQL
EstruturaMúltiplos endpointsUm único endpoint
DadosResposta fixaCliente define os campos
Over-fetchingComumResolvido
Under-fetchingRequer múltiplas chamadasResolvido
CacheSimples (HTTP cache)Mais complexo
AdoçãoUniversalCrescente

REST é mais simples e universal. GraphQL brilha quando há muitos tipos de clientes (mobile, web, TV) com necessidades diferentes de dados.

Boas Práticas

  • Use substantivos no plural nos endpoints: /usuarios, não /getUsuario
  • Retorne status codes corretos — 201 para criação, 204 para deleção sem corpo
  • Inclua paginação em todas as rotas que retornam listas
  • Use versionamento desde o início: /api/v1/
  • Documente com OpenAPI e mantenha a documentação atualizada
  • Implemente rate limiting para proteger a API
  • Use HTTPS sempre em produção

Erros Comuns

  • Usar POST para todas as operações (nível 0 de maturidade)
  • Retornar sempre 200 mesmo em erros
  • Colocar verbos na URL: /getArtigos, /deletarUsuario
  • Não implementar paginação em listas que podem crescer

Termos Relacionados

  • FastAPI - Framework moderno para APIs REST em Python
  • Flask - Microframework para APIs
  • Django - Framework web completo com suporte a APIs via DRF
  • Pydantic - Validação de dados essencial para APIs