404 Not Found

404 Not Found


nginx

Sistema de Injeção de Dependência — O Design Central do FastAPI

Injeção de dependência é como as interfaces de blocos de LEGO—cada bloco se preocupa apenas com o que deve fornecer e não precisa saber quem está usando. Conexões de banco de dados, o usuário atual e verificações de permissão são resolvidos automaticamente através de camadas aninhadas.

1. O Que Você Vai Aprender


2. A História Real da Alice

(1) Problema: Cada endpoint repete a obtenção de dados do banco de informações do usuário

O PriceTracker da Alice tem 20 endpoints, cada um dos quais requer abrir uma conexão de banco de dados, verificar um token JWT, obter o usuário atual e verificar permissões. Ela copia e cola as mesmas 15 linhas de código de inicialização em cada função de endpoint, então se o método de conexão com o banco de dados mudar, ela precisa fazer 20 modificações separadas.

(2) Soluções com Injeção de Dependência

A injeção de dependência do FastAPI extrai lógica repetitiva em funções de dependência reutilizáveis. Endpoints só precisam declarar db = Depends(get_db), e o FastAPI resolve e injeta automaticamente, incluindo resolução recursiva de subdependências.

PYTHON
from fastapi import Depends

async def get_db():
    db = Database()
    yield db
    db.close()

async def get_current_user(token: str, db=Depends(get_db)):
    return verify_token(token, db)

@app.get("/products")
async def list_products(user=Depends(get_current_user), db=Depends(get_db)):
    # user e db são injetados automaticamente
    ...

(3) Resultado

Conexões de banco de dados, autenticação de usuário e verificações de permissão—que antes exigiam 15 linhas de código para cada um dos 20 endpoints—foram consolidadas em três funções de dependência, então uma mudança feita em um lugar entra em vigor globalmente. Durante os testes, basta usar app.dependency_overrides[get_db] = lambda: mock_db para substituir a informação do banco de dados de todos os endpoints com uma única linha de código.


3. Noções Básicas do Depends()

(1) Dependências Funcionais

(1) ▶ Exemplo: Dependências de Função Simples

PYTHON
from fastapi import FastAPI, Depends

app = FastAPI()

def common_parameters(
    q: str | None = None,
    skip: int = 0,
    limit: int = 100,
):
    return {"q": q, "skip": skip, "limit": limit}

@app.get("/products")
async def list_products(commons: dict = Depends(common_parameters)):
    return commons

@app.get("/prices")
async def list_prices(commons: dict = Depends(common_parameters)):
    return commons

Saída:

TEXT
# Função definida com sucesso

(2) ▶ Exemplo: Dependências de Classe

PYTHON
from fastapi import FastAPI, Depends, Query

class CommonQueryParams:
    def __init__(
        self,
        q: str | None = Query(None),
        skip: int = Query(0, ge=0),
        limit: int = Query(100, ge=1, le=200),
    ):
        self.q = q
        self.skip = skip
        self.limit = limit

app = FastAPI()

@app.get("/products")
async def list_products(commons: CommonQueryParams = Depends(CommonQueryParams)):
    return {"q": commons.q, "skip": commons.skip, "limit": commons.limit}

Saída:

TEXT
# Função definida com sucesso

(2) Dependências de Função vs. Dependências de Classe

Dimensão Dependência de Função Dependência de Classe
Método de Definição def get_xxx() class Xxx: + __init__
Casos de Uso Lógica simples, chamada única Requer estado, múltiplos métodos
Análise de Parâmetros Análise Automática de Parâmetros de Função Análise Automática de Parâmetros do __init__
Reusabilidade Alta Média
Nível de Recomendação Primeira Escolha Usar em Cenários Complexos

4. Subdependências Aninhadas

(1) Árvore de Resolução de Dependências

100%
graph TD
    A[get_current_user] --> B[get_db]
    A --> C[get_token_from_header]
    C --> D[OAuth2PasswordBearer]
    
    E[require_admin] --> A
    E --> F[check_subscription]
    
    style A fill:#e1f5fe
    style E fill:#fff3e0

O FastAPI analisa recursivamente a árvore de dependências: require_adminget_current_userget_db, com cada dependência instanciada apenas uma vez (em cache na mesma requisição).

(1) ▶ Exemplo: Subdependências Aninhadas—DB → Usuário

PYTHON
from fastapi import FastAPI, Depends, HTTPException, Header

app = FastAPI()

# Nível 1: Sessão do banco de dados
async def get_db():
    db = {"connection": "active"}
    try:
        yield db
    finally:
        db["connection"] = "closed"

# Nível 2: Obter usuário atual (depende do DB)
async def get_current_user(
    authorization: str = Header(...),
    db: dict = Depends(get_db),
):
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid token")
    token = authorization.replace("Bearer ", "")
    # Simplificado: em produção, verificar JWT
    user = {"id": 1, "username": "alice", "role": "admin"}
    return user

# Nível 3: Exigir admin (depende do usuário atual)
async def require_admin(user: dict = Depends(get_current_user)):
    if user["role"] != "admin":
        raise HTTPException(status_code=403, detail="Admin required")
    return user

@app.get("/admin/stats")
async def admin_stats(admin: dict = Depends(require_admin)):
    return {"admin": admin["username"], "total_products": 1000000}

Saída:

TEXT
# Função definida com sucesso

5. Ciclo de Vida da Dependência e yield

(1) Nível de Requisição vs. Nível de Aplicação

Ciclo de Vida Método de Declaração Escopo Usos Típicos
Nível de Requisição def dep(): yield x; cleanup Criado e destruído em cada requisição DB Session, conexão Redis
Nível de Aplicação @app.on_event("startup") Criado quando a aplicação inicia Inicialização do Engine, pool de conexões

(1) ▶ Exemplo: Dependências yield e Limpeza de Recursos

PYTHON
from fastapi import FastAPI, Depends

app = FastAPI()

# Dependência com escopo de requisição e limpeza
async def get_db_session():
    # Configuração: criar sessão
    session = {"id": "session-123", "active": True}
    print(f"DB session opened: {session['id']}")
    try:
        yield session  # Isso é injetado no endpoint
    finally:
        # Limpeza: fechar sessão (executa após a resposta)
        session["active"] = False
        print(f"DB session closed: {session['id']}")

@app.get("/products")
async def list_products(db: dict = Depends(get_db_session)):
    print(f"Using DB session: {db['id']}")
    return {"session_id": db["id"]}

Saída (logs do servidor):

TEXT
DB session opened: session-123
Using DB session: session-123
DB session closed: session-123

6. Dependências Globais e Dependências de Grupo de Rotas

(1) Declarações de Dependência com Escopos Diferentes

(1) ▶ Exemplo: Dependências de Grupo de Rotas

PYTHON
from fastapi import FastAPI, Depends, APIRouter, Header, HTTPException

async def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key != "secret-key-123":
        raise HTTPException(status_code=401, detail="Invalid API key")
    return x_api_key

app = FastAPI()

# Rotas públicas - sem autenticação necessária
public_router = APIRouter()

@public_router.get("/health")
async def health():
    return {"status": "healthy"}

# Rotas protegidas - chave API necessária para todas as rotas neste grupo
protected_router = APIRouter(dependencies=[Depends(verify_api_key)])

@protected_router.get("/products")
async def list_products():
    return [{"id": 1, "name": "Widget"}]

@protected_router.post("/products")
async def create_product():
    return {"id": 2, "name": "New Product"}

# Registrar roteadores
app.include_router(public_router)
app.include_router(protected_router, prefix="/api/v1")

Saída:

TEXT
# Função definida com sucesso
Escopo de Dependência Local de Declaração Escopo de Efeito
Nível de Endpoint @app.get("/", dependencies=[...]) Endpoint Único
Nível de Grupo de Rotas APIRouter(dependencies=[...]) Todos os endpoints no grupo de rotas
Global FastAPI(dependencies=[...]) Todos os endpoints da aplicação

7. Substituição de Dependências e Testes

(1) dependency_overrides

Durante os testes, é necessário substituir dependências de produção (como conexões de banco de dados e APIs externas); app.dependency_overrides permite substituir implementações de dependência sem modificar o código-fonte.

(1) ▶ Exemplo: Substituindo Dependências de Banco de Dados em Testes

PYTHON
from fastapi.testclient import TestClient
from fastapi import FastAPI, Depends

app = FastAPI()

# Dependência real
async def get_db():
    return {"type": "postgresql", "host": "prod-db"}

# Dependência mock para testes
def get_mock_db():
    return {"type": "sqlite", "host": "memory"}

@app.get("/db-info")
async def db_info(db: dict = Depends(get_db)):
    return db

# Nos testes:
def test_db_info():
    app.dependency_overrides[get_db] = get_mock_db
    client = TestClient(app)
    response = client.get("/db-info")
    assert response.json() == {"type": "sqlite", "host": "memory"}
    # Limpar
    app.dependency_overrides.clear()

Saída:

TEXT
# Função definida com sucesso

❓ Perguntas Frequentes

P Dependências Depends são chamadas múltiplas vezes?
R Dentro da mesma requisição, cada dependência é executada apenas uma vez, e o resultado é em cache para reutilização. Para requisições diferentes, a dependência é re-executada.
P Quando a função de limpeza de uma dependência yield é executada?
R É executada após a resposta ser enviada ao cliente. Se o endpoint lançar uma exceção, a função de limpeza ainda é executada (similar a try/finally).
P Quão profundas as dependências podem ser aninhadas?
R Não há um limite estrito, mas aninhamento além de três níveis torna a depuração mais difícil. A cadeia DI de três níveis do PriceTracker (get_db → get_current_user → require_admin) é o limite superior recomendado.
P Dependências síncronas e assíncronas podem ser usadas juntas?
R Sim. O FastAPI lida automaticamente com dependências síncronas e assíncronas. Dependências síncronas rodam no pool de threads, enquanto dependências assíncronas rodam no event loop.
P dependency_overrides afeta outros testes?
R Sim, porque modifica o objeto app. Certifique-se de chamar app.dependency_overrides.clear() ou usar fixtures para gerenciá-lo após o teste ser concluído.
P Como os argumentos init são injetados para dependências de classe?
R Assim como com dependências de função, os argumentos init

📖 Resumo

  • Depends() extrai lógica repetitiva em funções auxiliares ou classes reutilizáveis; endpoints só precisam declará-las e não precisam se preocupar com a implementação
  • Resolução recursiva automática de subdependências; cache dentro da mesma requisição para evitar execuções duplicadas
  • yield implementa gerenciamento de ciclo de vida de nível de requisição para dependências, garantindo que criação e limpeza de recursos sejam pareadas
  • Dependências de grupo de rotas (APIRouter(dependencies=[...])) permitem que um grupo de endpoints compartilhe lógica de autenticação e validação
  • app.dependency_overrides é uma ferramenta de teste poderosa que substitui as dependências reais de todos os endpoints com uma única linha de código

📝 Exercícios

  1. Problema Básico (Dificuldade ⭐): Crie uma função auxiliar get_db que retorne um dicionário simulando uma conexão de banco de dados, e injete e use-a em ambos os endpoints. Dica: db: dict = Depends(get_db)
  2. Problema Avançado (Dificuldade ⭐⭐): Implemente dois níveis de dependências aninhadas: get_dbget_current_user (ler o cabeçalho Authorization da requisição), e injete as informações do usuário atual no endpoint /me. Dica: user: dict = Depends(get_current_user)
  3. Desafio (Dificuldade ⭐⭐⭐): Implemente uma cadeia DI de três camadas para o PriceTracker: get_db (yield + limpeza) → get_current_user (verificar token) → require_subscription("pro") (verificar nível de assinatura), e substitua a dependência DB com dependency_overrides durante os testes. Dica: dependência yield + app.dependency_overrides[get_db] = mock_fn

---|

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%