HTTP client (saída)¶
HTTPClient é um wrapper tipado sobre o httpx.AsyncClient para chamar
serviços externos com retry + backoff exponencial, circuit-breaker,
timeouts padrão e propagação do X-Request-ID. É a contraparte de saída do
middleware HTTP (que cuida do tráfego de entrada). Requer o extra
[http] (httpx).
Uso básico¶
O client é seguro pra compartilhar entre requests no mesmo event loop — ele
reusa o connection pool interno. Use como async context manager (ou guarde
um singleton em resources.py e feche no lifespan).
from typing import Any
from tempest_fastapi_sdk import HTTPClient
client = HTTPClient(base_url="https://api.example.com", timeout=10.0)
async def fetch_user(user_id: str) -> dict[str, Any]:
"""GET /users/{id} no serviço externo."""
async with client:
response = await client.get(f"/users/{user_id}")
response.raise_for_status()
return response.json()
Métodos: get / post / put / patch / delete (e request genérico),
todos repassando kwargs pro httpx (json=, params=, headers=, ...) e
devolvendo um httpx.Response.
Retry + backoff + circuit-breaker¶
Passe um RetryPolicy e ajuste os limites do breaker na construção:
from tempest_fastapi_sdk import CircuitOpenError, HTTPClient, RetryPolicy
client = HTTPClient(
base_url="https://api.example.com",
timeout=5.0,
retry_policy=RetryPolicy(
max_attempts=3, # 1 tentativa + 2 retries
backoff_initial_seconds=0.5, # 0.5s, 1s, 2s... (exponencial)
backoff_max_seconds=8.0, # teto por espera
),
failure_threshold=5, # abre o circuito após 5 falhas seguidas
recovery_seconds=30.0, # meio-aberto após 30s
default_headers={"X-Api-Key": "..."},
propagate_request_id=True, # encaminha o X-Request-ID do request atual
)
async def call() -> None:
try:
async with client:
await client.post("/charge", json={"amount": 100})
except CircuitOpenError:
# O circuito está aberto — não martele o upstream caído.
...
- Retry: refeito em erros transitórios (timeouts, 5xx, falhas de conexão)
até
max_attempts, com backoff exponencial limitado porbackoff_max_seconds. - Circuit-breaker: após
failure_thresholdfalhas consecutivas o circuito abre e as chamadas levantamCircuitOpenErrorimediatamente (sem tocar a rede) até passarrecovery_seconds, quando entra em meio-aberto pra testar. - Request-ID: com
propagate_request_id=True, oX-Request-IDdo request em curso (viaRequestIDMiddleware) é repassado ao upstream, costurando os logs ponta-a-ponta.
Guarde como singleton em resources.py
Crie o HTTPClient uma vez (em src/api/dependencies/resources.py),
exponha um get_http_client, e feche no lifespan com await client.aclose()
— assim o connection pool é reaproveitado entre requests.
Recap¶
HTTPClient=httpx.AsyncClienttipado + retry/backoff/circuit-breaker + X-Request-ID.- Extra
[http]. Métodosget/post/put/patch/delete/request→httpx.Response. RetryPolicy(max_attempts, backoff_initial_seconds, backoff_max_seconds)controla o retry.failure_threshold/recovery_secondscontrolam o breaker;CircuitOpenErrorquando aberto.- Compartilhe um singleton e feche com
aclose()no shutdown.