Voltar ao Glossario
Glossario Python

FastAPI: O que É e Como Funciona | Python Brasil

Guia completo do FastAPI: framework Python para APIs de alta performance com validação automática, async nativo, JWT e deploy com uvicorn.

O que é FastAPI?

O FastAPI é um framework web moderno e de alta performance para construir APIs com Python. Criado por Sebastián Ramírez e lançado em 2018, ele se baseia em type hints do Python e no Pydantic para oferecer validação automática de dados, documentação interativa e desempenho comparável ao Node.js e Go.

O FastAPI se tornou um dos projetos Python de crescimento mais rápido da história, sendo adotado por empresas como Microsoft, Uber, Netflix e Mercado Livre.

Como Funciona: Starlette + Pydantic

O FastAPI é construído sobre duas fundações:

  • Starlette: framework ASGI assíncrono que cuida do roteamento, middlewares, WebSockets e requisições HTTP. É extremamente rápido e leve.
  • Pydantic: biblioteca de validação de dados que garante que os dados recebidos e enviados estejam no formato correto, com mensagens de erro claras e automáticas.

Essa combinação permite que o FastAPI seja ao mesmo tempo rápido (Starlette + ASGI), seguro (Pydantic + type hints) e produtivo (documentação automática + autocompletar no editor).

Exemplo Básico

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
from typing import Optional

app = FastAPI(title="Minha API", version="1.0.0")

class Produto(BaseModel):
    nome: str
    preco: float
    em_estoque: bool = True
    descricao: Optional[str] = None

# Banco em memória para o exemplo
produtos: dict[int, Produto] = {}
proximo_id = 1

@app.get("/")
async def raiz():
    return {"mensagem": "Bem-vindo à API!"}

@app.get("/produtos", response_model=list[Produto])
async def listar_produtos():
    return list(produtos.values())

@app.get("/produtos/{produto_id}", response_model=Produto)
async def buscar_produto(produto_id: int):
    if produto_id not in produtos:
        raise HTTPException(status_code=404, detail="Produto não encontrado")
    return produtos[produto_id]

@app.post("/produtos", response_model=Produto, status_code=status.HTTP_201_CREATED)
async def criar_produto(produto: Produto):
    global proximo_id
    produtos[proximo_id] = produto
    proximo_id += 1
    return produto

@app.delete("/produtos/{produto_id}", status_code=status.HTTP_204_NO_CONTENT)
async def deletar_produto(produto_id: int):
    if produto_id not in produtos:
        raise HTTPException(status_code=404, detail="Produto não encontrado")
    del produtos[produto_id]

Sistema de Injeção de Dependências

O sistema de dependency injection do FastAPI é um de seus recursos mais poderosos. Permite reutilizar lógica como autenticação, sessões de banco de dados e paginação de forma limpa.

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from typing import Generator

app = FastAPI()

# Dependência de banco de dados
def get_db() -> Generator:
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# Dependência de paginação reutilizável
class Paginacao:
    def __init__(self, pagina: int = 1, tamanho: int = 20):
        self.offset = (pagina - 1) * tamanho
        self.limit = tamanho

# Dependência de autenticação
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")

async def usuario_atual(token: str = Depends(oauth2_scheme)):
    # Valida o token e retorna o usuário
    usuario = verificar_token(token)
    if not usuario:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token inválido",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return usuario

@app.get("/produtos")
async def listar_produtos(
    paginacao: Paginacao = Depends(),
    db: Session = Depends(get_db),
    usuario = Depends(usuario_atual),
):
    return db.query(Produto).offset(paginacao.offset).limit(paginacao.limit).all()

Autenticação com JWT

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta

SECRET_KEY = "sua-chave-secreta"
ALGORITHM = "HS256"
EXPIRACAO_TOKEN = 30  # minutos

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")

def criar_token(dados: dict) -> str:
    payload = dados.copy()
    payload["exp"] = datetime.utcnow() + timedelta(minutes=EXPIRACAO_TOKEN)
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

@app.post("/auth/token")
async def login(form: OAuth2PasswordRequestForm = Depends()):
    usuario = autenticar_usuario(form.username, form.password)
    if not usuario:
        raise HTTPException(status_code=400, detail="Credenciais inválidas")
    token = criar_token({"sub": usuario.email})
    return {"access_token": token, "token_type": "bearer"}

Tarefas em Background

O FastAPI permite executar tarefas em background sem bloquear a resposta:

from fastapi import FastAPI, BackgroundTasks
import smtplib

app = FastAPI()

def enviar_email_boas_vindas(email: str, nome: str):
    # Função executada em segundo plano
    print(f"Enviando e-mail de boas-vindas para {nome} ({email})")
    # ... lógica de envio de e-mail

@app.post("/usuarios", status_code=201)
async def criar_usuario(usuario: UsuarioCreate, background_tasks: BackgroundTasks):
    novo_usuario = salvar_usuario(usuario)
    background_tasks.add_task(
        enviar_email_boas_vindas,
        email=usuario.email,
        nome=usuario.nome
    )
    return novo_usuario

Middleware e CORS

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
import time

app = FastAPI()

# CORS para frontends
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://meusite.com.br", "http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Middleware customizado
@app.middleware("http")
async def adicionar_tempo_resposta(request, call_next):
    inicio = time.time()
    response = await call_next(request)
    duracao = time.time() - inicio
    response.headers["X-Process-Time"] = str(duracao)
    return response

Integração com Banco de Dados (SQLAlchemy Assíncrono)

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from fastapi import Depends

DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/dbname"
engine = create_async_engine(DATABASE_URL)

class Base(DeclarativeBase):
    pass

class Produto(Base):
    __tablename__ = "produtos"
    id: Mapped[int] = mapped_column(primary_key=True)
    nome: Mapped[str] = mapped_column(nullable=False)
    preco: Mapped[float]

async def get_session() -> AsyncSession:
    async with AsyncSession(engine) as session:
        yield session

@app.get("/produtos/{produto_id}")
async def buscar_produto(produto_id: int, db: AsyncSession = Depends(get_session)):
    produto = await db.get(Produto, produto_id)
    if not produto:
        raise HTTPException(status_code=404, detail="Não encontrado")
    return produto

Testando com TestClient

from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_listar_produtos():
    resposta = client.get("/produtos")
    assert resposta.status_code == 200
    assert isinstance(resposta.json(), list)

def test_criar_produto():
    resposta = client.post("/produtos", json={
        "nome": "Notebook",
        "preco": 4999.90,
        "em_estoque": True
    })
    assert resposta.status_code == 201
    assert resposta.json()["nome"] == "Notebook"

def test_produto_nao_encontrado():
    resposta = client.get("/produtos/99999")
    assert resposta.status_code == 404

Deploy com Uvicorn e Gunicorn

# Desenvolvimento
uvicorn main:app --reload --port 8000

# Produção com múltiplos workers
gunicorn main:app \
  --workers 4 \
  --worker-class uvicorn.workers.UvicornWorker \
  --bind 0.0.0.0:8000

# Docker
# Dockerfile
# FROM python:3.12-slim
# WORKDIR /app
# COPY requirements.txt .
# RUN pip install -r requirements.txt
# COPY . .
# CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Performance

O FastAPI é um dos frameworks Python mais rápidos disponíveis. Benchmarks independentes mostram performance comparável a Node.js e Go em cenários de I/O intensivo, graças ao modelo assíncrono baseado em ASGI e ao processamento eficiente do Pydantic v2 (escrito em Rust).

Erros Comuns

  • Misturar funções sync e async: funções def comuns bloqueiam o event loop; use async def para operações de I/O
  • Não usar response_model: sem ele, dados sensíveis podem vazar na resposta
  • Dependências com estado global: evite variáveis globais mutáveis; prefira dependências injetadas
  • Esquecer validação de entrada: confie no Pydantic para validar dados antes de processar

Boas Práticas

  • Organize o projeto em routers separados por domínio
  • Use response_model em todas as rotas para controlar o que é retornado
  • Documente parâmetros com Query, Path e Body do FastAPI
  • Use variáveis de ambiente com pydantic-settings para configuração
  • Escreva testes com TestClient antes de subir para produção

Termos Relacionados

  • Pydantic - Validação de dados usada pelo FastAPI
  • Async/Await - Programação assíncrona em Python
  • Type Hints - Tipagem estática que potencializa o FastAPI
  • API REST - Padrão de APIs RESTful