Python Admin

FastAPI em Python: Fundamentos, Roteamento e Validação com Pydantic na Prática Já leu

Introdução ao FastAPI FastAPI é um framework web moderno para construir APIs REST em Python, lançado em 2018 por Sebastián Ramírez. Diferentemente de frameworks mais antigos como Flask ou Django, FastAPI foi construído sobre especificações modernas (ASGI) e aproveita type hints do Python 3.6+ para validação automática de dados, geração de documentação interativa e melhor performance. A grande vantagem do FastAPI está em sua abordagem zero boilerplate: você escreve código simples e declarativo, e o framework cuida automaticamente de validação, serialização, documentação OpenAPI e muito mais. Se você já usou frameworks anteriores, vai se surpreender com a quantidade de trabalho que FastAPI elimina. A velocidade de desenvolvimento é comparável à do Flask, mas com segurança e validação de nível enterprise. Fundamentos: Setup e Sua Primeira Aplicação Instalação e Configuração Inicial Para começar, você precisa instalar o FastAPI e um servidor ASGI. A forma mais comum é usar Uvicorn, que é um servidor ASGI de alta performance escrito em Python: Após

Introdução ao FastAPI

FastAPI é um framework web moderno para construir APIs REST em Python, lançado em 2018 por Sebastián Ramírez. Diferentemente de frameworks mais antigos como Flask ou Django, FastAPI foi construído sobre especificações modernas (ASGI) e aproveita type hints do Python 3.6+ para validação automática de dados, geração de documentação interativa e melhor performance.

A grande vantagem do FastAPI está em sua abordagem zero boilerplate: você escreve código simples e declarativo, e o framework cuida automaticamente de validação, serialização, documentação OpenAPI e muito mais. Se você já usou frameworks anteriores, vai se surpreender com a quantidade de trabalho que FastAPI elimina. A velocidade de desenvolvimento é comparável à do Flask, mas com segurança e validação de nível enterprise.

Fundamentos: Setup e Sua Primeira Aplicação

Instalação e Configuração Inicial

Para começar, você precisa instalar o FastAPI e um servidor ASGI. A forma mais comum é usar Uvicorn, que é um servidor ASGI de alta performance escrito em Python:

pip install fastapi uvicorn[standard]

Após a instalação, crie um arquivo chamado main.py com o seguinte código:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"mensagem": "Olá, mundo!"}

Para executar a aplicação:

uvicorn main:app --reload

O parâmetro --reload reinicia o servidor automaticamente quando você modifica o código (útil durante desenvolvimento). A aplicação estará disponível em http://localhost:8000. Acesse também http://localhost:8000/docs para ver a documentação interativa Swagger gerada automaticamente.

Entendendo a Estrutura Básica

Um projeto FastAPI típico é bastante simples: você cria uma instância da classe FastAPI, depois decora funções Python com decoradores HTTP (@app.get, @app.post, etc.). Cada função decorada é uma operação (endpoint) que pode receber parâmetros e retornar dados. O retorno é automaticamente convertido para JSON.

Quando você acessa um endpoint, o FastAPI executa a função correspondente e serializa o retorno. Isso é possível porque Python 3.6+ suporta type hints, permitindo que o framework saiba exatamente que tipo de dado esperar e retornar.

from fastapi import FastAPI
from datetime import datetime

app = FastAPI()

@app.get("/info")
def get_info():
    return {
        "timestamp": datetime.now(),
        "status": "online"
    }

Quando você faz uma requisição GET para /info, FastAPI serializa automaticamente o datetime para string ISO 8601. Sem type hints explícitos, isso ainda funcionaria, mas você perderia validação e documentação automática.

Roteamento Avançado

Parâmetros de Rota e Query

FastAPI diferencia automaticamente parâmetros de rota (path parameters) de query parameters baseado em como você declara a função. Um parâmetro declarado na string da rota é um path parameter; um parâmetro da função que não está na rota é um query parameter.

from fastapi import FastAPI

app = FastAPI()

# /usuarios/123 -> id=123 (path parameter)
@app.get("/usuarios/{id}")
def get_usuario(id: int):
    return {"usuario_id": id, "nome": "João"}

# /produtos?categoria=eletrônicos&limite=10
@app.get("/produtos")
def listar_produtos(categoria: str, limite: int = 10):
    return {
        "categoria": categoria,
        "limite": limite,
        "produtos": []
    }

Note que limite tem um valor padrão (10). Isso o torna opcional na query string. Se o cliente não enviar ?limite=..., o valor padrão será usado. FastAPI valida automaticamente: se o cliente enviar ?limite=abc, você receberá um erro 422 (Unprocessable Entity) com mensagem clara sobre o tipo esperado.

Múltiplos Métodos HTTP no Mesmo Caminho

É comum ter diferentes operações no mesmo caminho, usando métodos HTTP diferentes. FastAPI suporta todos os métodos padrão:

from fastapi import FastAPI

app = FastAPI()

# Simulando um banco de dados em memória
usuarios = [
    {"id": 1, "nome": "Alice", "email": "alice@example.com"},
    {"id": 2, "nome": "Bob", "email": "bob@example.com"}
]

@app.get("/usuarios/{id}")
def obter_usuario(id: int):
    for usuario in usuarios:
        if usuario["id"] == id:
            return usuario
    return {"erro": "Usuário não encontrado"}

@app.put("/usuarios/{id}")
def atualizar_usuario(id: int, nome: str, email: str):
    for usuario in usuarios:
        if usuario["id"] == id:
            usuario["nome"] = nome
            usuario["email"] = email
            return usuario
    return {"erro": "Usuário não encontrado"}

@app.delete("/usuarios/{id}")
def deletar_usuario(id: int):
    global usuarios
    usuarios = [u for u in usuarios if u["id"] != id]
    return {"mensagem": "Usuário deletado"}

Path Parameters com Validação

Você pode adicionar validação diretamente aos parâmetros usando Path:

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/posts/{post_id}")
def obter_post(
    post_id: int = Path(..., gt=0, description="ID do post deve ser positivo")
):
    return {"post_id": post_id, "titulo": "Meu Post"}

O parâmetro gt=0 (greater than) valida que post_id deve ser maior que zero. Se o cliente enviar /posts/-1, receberá um erro 422 com mensagem automática. O ... (Ellipsis) indica que o parâmetro é obrigatório.

Validação com Pydantic

Modelos Pydantic para Request Bodies

Quando você precisa aceitar dados complexos no corpo da requisição (POST, PUT), use modelos Pydantic. Um modelo Pydantic é uma classe que herda de BaseModel e define a estrutura esperada dos dados:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Usuario(BaseModel):
    nome: str
    email: str
    idade: int
    ativo: bool = True

@app.post("/usuarios")
def criar_usuario(usuario: Usuario):
    return {
        "mensagem": "Usuário criado com sucesso",
        "usuario": usuario
    }

Quando um cliente faz uma requisição POST para /usuarios com JSON no corpo, FastAPI automaticamente:
1. Valida que o JSON contém nome (string), email (string) e idade (inteiro)
2. Valida que ativo é boolean, ou usa o padrão True se não foi enviado
3. Cria uma instância de Usuario com os dados
4. Passa essa instância para a função

Se o cliente enviar dados inválidos (por exemplo, idade: "abc"), recebe imediatamente um erro 422 com detalhes sobre qual campo está errado.

Validação Avançada com Field

Para validações mais complexas, use Field do Pydantic:

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class Produto(BaseModel):
    nome: str = Field(..., min_length=3, max_length=100)
    preco: float = Field(..., gt=0, le=1000000)
    descricao: str = Field(None, max_length=500)
    estoque: int = Field(default=0, ge=0)

@app.post("/produtos")
def criar_produto(produto: Produto):
    return {
        "mensagem": "Produto criado",
        "produto": produto
    }

Aqui:
- nome deve ter entre 3 e 100 caracteres
- preco deve ser maior que 0 e no máximo 1.000.000
- descricao é opcional (pode ser None) e até 500 caracteres
- estoque começa em 0 se não for fornecido, e deve ser >= 0

Modelos Aninhados e Listas

Seus modelos Pydantic podem conter outros modelos Pydantic, criando estruturas hierárquicas:

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

app = FastAPI()

class Endereco(BaseModel):
    rua: str
    cidade: str
    cep: str

class Usuario(BaseModel):
    nome: str
    email: str
    endereco: Endereco

class Pedido(BaseModel):
    usuarios: List[Usuario]
    total: float

@app.post("/pedidos")
def criar_pedido(pedido: Pedido):
    return {
        "mensagem": "Pedido recebido",
        "quantidade_usuarios": len(pedido.usuarios),
        "total": pedido.total
    }

Quando um cliente envia um JSON como este, FastAPI valida recursivamente toda a hierarquia:

{
  "usuarios": [
    {
      "nome": "Alice",
      "email": "alice@example.com",
      "endereco": {
        "rua": "Rua A, 123",
        "cidade": "São Paulo",
        "cep": "01000-000"
      }
    }
  ],
  "total": 99.99
}

Validadores Customizados

Às vezes validação simples não é suficiente. Pydantic permite definir validadores customizados:

from fastapi import FastAPI
from pydantic import BaseModel, validator
import re

app = FastAPI()

class Usuario(BaseModel):
    nome: str
    email: str
    telefone: str

    @validator('email')
    def validar_email(cls, v):
        if '@' not in v or '.' not in v:
            raise ValueError('Email inválido')
        return v

    @validator('telefone')
    def validar_telefone(cls, v):
        apenas_numeros = re.sub(r'\D', '', v)
        if len(apenas_numeros) < 10:
            raise ValueError('Telefone deve ter pelo menos 10 dígitos')
        return v

@app.post("/usuarios")
def criar_usuario(usuario: Usuario):
    return usuario

O decorador @validator recebe o nome do campo e a função é chamada automaticamente durante validação. Se você raise uma ValueError, Pydantic converte em erro 422 automático.

Resposta Estruturada e Documentação

Modelos de Resposta

É boa prática definir modelos Pydantic também para as respostas, garantindo que a documentação automática seja precisa:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Usuario(BaseModel):
    id: int
    nome: str
    email: str

class UsuarioResposta(BaseModel):
    mensagem: str
    usuario: Usuario

@app.get("/usuarios/{id}", response_model=UsuarioResposta)
def obter_usuario(id: int):
    usuario = {"id": id, "nome": "João", "email": "joao@example.com"}
    return {
        "mensagem": "Usuário encontrado",
        "usuario": usuario
    }

O parâmetro response_model informa a FastAPI exatamente que estrutura será retornada. Isso:
1. Valida a resposta antes de enviar (se houver inconsistência, você descobre no desenvolvimento)
2. Documenta automaticamente a resposta no Swagger
3. Permite exclusão automática de campos sensíveis

Documentação Interativa

FastAPI gera documentação automática baseada em seus modelos e type hints. Existem dois formatos padrão:

  • Swagger UI: http://localhost:8000/docs (mais visual)
  • ReDoc: http://localhost:8000/redoc (mais clara para leitura)

Para adicionar descrições às suas operações:

from fastapi import FastAPI

app = FastAPI(
    title="API de Usuários",
    description="Gerencia usuários da aplicação",
    version="1.0.0"
)

@app.get(
    "/usuarios/{id}",
    summary="Obter um usuário",
    description="Retorna os dados de um usuário específico pelo ID",
    tags=["Usuários"]
)
def obter_usuario(id: int):
    return {"id": id, "nome": "João"}

Os parâmetros summary, description e tags aparecem na documentação interativa. Você também pode documentar parâmetros:

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/buscar")
def buscar(
    q: str = Query(..., min_length=3, description="Termo de busca com mínimo 3 caracteres")
):
    return {"busca": q}

Conclusão

Nesta aula, cobrimos os fundamentos essenciais para dominar FastAPI. Primeiro, aprendemos que FastAPI é um framework ASGI moderno que elimina boilerplate através de type hints, oferecendo validação, documentação e serialização automáticas—o que o diferencia fundamentalmente de frameworks mais antigos. Segundo, entendemos que o roteamento no FastAPI é intuitivo e expressivo: parâmetros de rota e query são declarados naturalmente na assinatura da função, e validação acontece automaticamente. Por último, dominamos Pydantic não apenas para receber dados (request bodies), mas como forma de documentar e validar suas APIs de maneira declarativa, criando modelos reutilizáveis, aninhados, com validadores customizados e resposta estruturada.

Referências


Artigos relacionados