Python

Guia Completo de FastAPI Avançado: Depends, Middleware, Background Tasks e Websockets

26 min de leitura

Guia Completo de FastAPI Avançado: Depends, Middleware, Background Tasks e Websockets

Dependency Injection com Depends A injeção de dependências é um padrão fundamental em arquiteturas bem construídas. No FastAPI, o sistema de permite que você declare dependências de forma limpa e reutilizável, evitando código duplicado e melhorando a testabilidade. Diferentemente de frameworks mais antigos, FastAPI resolve dependências automaticamente durante a execução da requisição, injetando-as nos parâmetros da sua função manipuladora. O conceito funciona assim: você cria funções que retornam valores ou objetos que serão injetados em outras funções. Essas funções podem ter suas próprias dependências, criando uma árvore de resoluções que o FastAPI processa de forma inteligente, inclusive cache para requisições da mesma função dentro do mesmo escopo. Dependências Simples As dependências mais básicas são funções que retornam um valor específico. Imagine validar um token ou recuperar um usuário do banco de dados: Neste exemplo, é uma dependência que valida se o usuário existe. Quando uma rota usa , o FastAPI chama a função automaticamente e passa seu resultado. Se a

<h2>Dependency Injection com Depends</h2>

<p>A injeção de dependências é um padrão fundamental em arquiteturas bem construídas. No FastAPI, o sistema de <code>Depends</code> permite que você declare dependências de forma limpa e reutilizável, evitando código duplicado e melhorando a testabilidade. Diferentemente de frameworks mais antigos, FastAPI resolve dependências automaticamente durante a execução da requisição, injetando-as nos parâmetros da sua função manipuladora.</p>

<p>O conceito funciona assim: você cria funções que retornam valores ou objetos que serão injetados em outras funções. Essas funções podem ter suas próprias dependências, criando uma árvore de resoluções que o FastAPI processa de forma inteligente, inclusive cache para requisições da mesma função dentro do mesmo escopo.</p>

<h3>Dependências Simples</h3>

<p>As dependências mais básicas são funções que retornam um valor específico. Imagine validar um token ou recuperar um usuário do banco de dados:</p>

<pre><code class="language-python">from fastapi import FastAPI, Depends, HTTPException, status

app = FastAPI()

Simulando um banco de dados em memória

users_db = {

&quot;user123&quot;: {&quot;id&quot;: &quot;user123&quot;, &quot;name&quot;: &quot;João Silva&quot;, &quot;email&quot;: &quot;joao@example.com&quot;}

}

def get_current_user(user_id: str = &quot;user123&quot;) -&gt; dict:

&quot;&quot;&quot;Dependência que busca o usuário atual.&quot;&quot;&quot;

if user_id not in users_db:

raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=&quot;Usuário não encontrado&quot;)

return users_db[user_id]

@app.get(&quot;/profile&quot;)

def get_profile(current_user: dict = Depends(get_current_user)):

&quot;&quot;&quot;Endpoint que usa a dependência de usuário.&quot;&quot;&quot;

return {&quot;profile&quot;: current_user, &quot;message&quot;: f&quot;Bem-vindo, {current_user[&#039;name&#039;]}&quot;}</code></pre>

<p>Neste exemplo, <code>get_current_user</code> é uma dependência que valida se o usuário existe. Quando uma rota usa <code>Depends(get_current_user)</code>, o FastAPI chama a função automaticamente e passa seu resultado. Se a função lançar uma exceção, o FastAPI trata e retorna a resposta HTTP apropriada.</p>

<h3>Dependências com Sub-dependências</h3>

<p>Uma dependência pode depender de outras dependências. Isso cria uma hierarquia que o FastAPI resolve recursivamente:</p>

<pre><code class="language-python">from fastapi import FastAPI, Depends, HTTPException, status

from typing import Optional

app = FastAPI()

def verify_token(authorization: Optional[str] = None) -&gt; str:

&quot;&quot;&quot;Verifica se o token está presente.&quot;&quot;&quot;

if not authorization or not authorization.startswith(&quot;Bearer &quot;):

raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=&quot;Token inválido&quot;)

return authorization.replace(&quot;Bearer &quot;, &quot;&quot;)

def get_current_user(token: str = Depends(verify_token)) -&gt; dict:

&quot;&quot;&quot;Dependência que verifica o token e retorna o usuário.&quot;&quot;&quot;

Em produção, você decodificaria o JWT aqui

if token == &quot;valid_token_123&quot;:

return {&quot;id&quot;: &quot;user1&quot;, &quot;name&quot;: &quot;Maria&quot;, &quot;email&quot;: &quot;maria@example.com&quot;}

raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=&quot;Token expirado&quot;)

@app.get(&quot;/secure-data&quot;)

def get_secure_data(current_user: dict = Depends(get_current_user)):

&quot;&quot;&quot;Endpoint protegido que usa sub-dependências.&quot;&quot;&quot;

return {&quot;data&quot;: &quot;Informação confidencial&quot;, &quot;user&quot;: current_user[&quot;name&quot;]}</code></pre>

<p>Aqui, <code>get_current_user</code> depende de <code>verify_token</code>. O FastAPI chama <code>verify_token</code> primeiro, depois passa seu resultado para <code>get_current_user</code>. Se qualquer dependência falhar, a cadeia inteira é interrompida.</p>

<h3>Dependências de Classe</h3>

<p>Para casos mais complexos, você pode usar classes como dependências. Isso é particularmente útil quando você precisa reutilizar lógica ou estado:</p>

<pre><code class="language-python">from fastapi import FastAPI, Depends, HTTPException, status

app = FastAPI()

class PaginationParams:

&quot;&quot;&quot;Classe para parametrização de paginação.&quot;&quot;&quot;

def __init__(self, skip: int = 0, limit: int = 10):

if skip &lt; 0:

raise HTTPException(status_code=400, detail=&quot;skip não pode ser negativo&quot;)

if limit &lt;= 0 or limit &gt; 100:

raise HTTPException(status_code=400, detail=&quot;limit deve estar entre 1 e 100&quot;)

self.skip = skip

self.limit = limit

@app.get(&quot;/items&quot;)

def list_items(pagination: PaginationParams = Depends()):

&quot;&quot;&quot;Endpoint que usa classe como dependência.&quot;&quot;&quot;

items = [f&quot;item_{i}&quot; for i in range(pagination.skip, pagination.skip + pagination.limit)]

return {&quot;skip&quot;: pagination.skip, &quot;limit&quot;: pagination.limit, &quot;items&quot;: items}</code></pre>

<p>Quando você usa <code>Depends()</code> sem argumentos em uma classe, o FastAPI automaticamente chama o método <code>__init__</code> e injeta os parâmetros da URL/query. Isso é extremamente elegante para parametrização reutilizável.</p>

<h2>Middleware: Processando Requisições Globalmente</h2>

<p>Middleware são funções que processam requisições e respostas em um nível global, antes que cheguem aos endpoints e depois que saem deles. Diferentemente de dependências, que são específicas de rotas, middleware afeta toda a aplicação. Você pode usá-los para logging, autenticação global, modificação de headers, ou tratamento transversal de erros.</p>

<p>No FastAPI (que usa Starlette internamente), você adiciona middleware usando o padrão <code>@app.middleware(&quot;http&quot;)</code>. A função middleware recebe a requisição, passa para a próxima camada da aplicação, captura a resposta e pode modificá-la antes de retornar.</p>

<h3>Middleware Básico de Logging</h3>

<p>Um caso de uso clássico é registrar informações sobre as requisições:</p>

<pre><code class="language-python">from fastapi import FastAPI

from starlette.middleware.base import BaseHTTPMiddleware

from starlette.requests import Request

import time

import logging

app = FastAPI()

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

@app.middleware(&quot;http&quot;)

async def log_requests(request: Request, call_next):

&quot;&quot;&quot;Middleware que registra todas as requisições.&quot;&quot;&quot;

start_time = time.time()

Processa a requisição

response = await call_next(request)

Calcula tempo de processamento

process_time = time.time() - start_time

Adiciona header customizado

response.headers[&quot;X-Process-Time&quot;] = str(process_time)

logger.info(

f&quot;Método: {request.method} | Path: {request.url.path} | &quot; f&quot;Status: {response.status_code} | Tempo: {process_time:.2f}s&quot;

)

return response

@app.get(&quot;/hello&quot;)

def hello():

return {&quot;message&quot;: &quot;Olá, mundo!&quot;}</code></pre>

<p>Este middleware captura o tempo de início, passa a requisição adiante, e depois adiciona o tempo de processamento como header na resposta. Todos os endpoints da aplicação passarão por esse middleware automaticamente.</p>

<h3>Middleware de Autenticação Global</h3>

<p>Você pode usar middleware para aplicar autenticação em toda a aplicação, sem precisar adicionar <code>Depends</code> em cada rota:</p>

<pre><code class="language-python">from fastapi import FastAPI, HTTPException, status

from starlette.middleware.base import BaseHTTPMiddleware

from starlette.requests import Request

from starlette.responses import JSONResponse

app = FastAPI()

class AuthMiddleware(BaseHTTPMiddleware):

&quot;&quot;&quot;Middleware customizado que verifica autenticação.&quot;&quot;&quot;

async def dispatch(self, request: Request, call_next):

Rotas públicas que não precisam de autenticação

public_paths = [&quot;/health&quot;, &quot;/docs&quot;, &quot;/openapi.json&quot;]

if request.url.path in public_paths:

return await call_next(request)

Verifica o header de autorização

auth_header = request.headers.get(&quot;Authorization&quot;)

if not auth_header or not auth_header.startswith(&quot;Bearer &quot;):

return JSONResponse(

status_code=status.HTTP_401_UNAUTHORIZED,

content={&quot;detail&quot;: &quot;Credenciais inválidas&quot;}

)

token = auth_header.replace(&quot;Bearer &quot;, &quot;&quot;)

if token != &quot;secret_token_123&quot;:

return JSONResponse(

status_code=status.HTTP_401_UNAUTHORIZED,

content={&quot;detail&quot;: &quot;Token inválido&quot;}

)

Se passou na validação, prossegue

response = await call_next(request)

return response

Adiciona o middleware à aplicação

app.add_middleware(AuthMiddleware)

@app.get(&quot;/health&quot;)

def health_check():

&quot;&quot;&quot;Rota pública.&quot;&quot;&quot;

return {&quot;status&quot;: &quot;ok&quot;}

@app.get(&quot;/protected&quot;)

def protected_route():

&quot;&quot;&quot;Rota protegida que requer token.&quot;&quot;&quot;

return {&quot;data&quot;: &quot;Este é um dados protegidos&quot;}</code></pre>

<p>Note que middleware são processados em ordem inversa de adição. Se você adicionar múltiplos middleware, o último adicionado será o primeiro a processar a requisição.</p>

<h3>Middleware para Modificar Respostas</h3>

<p>Você também pode usar middleware para modificar o corpo da resposta, adicionar informações ou transformar dados:</p>

<pre><code class="language-python">from fastapi import FastAPI

from starlette.middleware.base import BaseHTTPMiddleware

from starlette.requests import Request

from starlette.responses import Response

import json

app = FastAPI()

class ResponseWrapperMiddleware(BaseHTTPMiddleware):

&quot;&quot;&quot;Middleware que envolve todas as respostas em um padrão.&quot;&quot;&quot;

async def dispatch(self, request: Request, call_next):

response = await call_next(request)

Lê o corpo da resposta

body = b&quot;&quot;

async for chunk in response.body_iterator:

body += chunk

Decodifica e envolve

try:

data = json.loads(body.decode())

except:

data = body.decode()

wrapped = {

&quot;success&quot;: 200 &lt;= response.status_code &lt; 300,

&quot;status_code&quot;: response.status_code,

&quot;data&quot;: data

}

Retorna nova resposta

return Response(

content=json.dumps(wrapped),

status_code=response.status_code,

media_type=&quot;application/json&quot;

)

app.add_middleware(ResponseWrapperMiddleware)

@app.get(&quot;/example&quot;)

def example():

return {&quot;message&quot;: &quot;Olá&quot;}</code></pre>

<p>Quando você chama <code>GET /example</code>, a resposta será envelopada automaticamente:</p>

<pre><code class="language-json">{

&quot;success&quot;: true,

&quot;status_code&quot;: 200,

&quot;data&quot;: {&quot;message&quot;: &quot;Olá&quot;}

}</code></pre>

<h2>Background Tasks: Executar Operações Assincronamente</h2>

<p>Background tasks (tarefas em background) permitem que você inicie operações que não precisam completar antes de retornar a resposta ao cliente. Isso é fundamental para melhorar a experiência do usuário: você responde imediatamente e processa operações demoradas (envio de email, processamento pesado, etc.) em segundo plano.</p>

<p>FastAPI oferece duas abordagens principais: <code>BackgroundTasks</code> para tarefas simples na mesma aplicação, e Celery/RQ para sistemas distribuídos mais robustos. Começaremos com <code>BackgroundTasks</code>, que já vem integrada.</p>

<h3>Background Tasks Básicas</h3>

<p>A maneira mais simples é adicionar um parâmetro <code>BackgroundTasks</code> ao seu endpoint:</p>

<pre><code class="language-python">from fastapi import FastAPI, BackgroundTasks

import time

app = FastAPI()

def send_email(email: str, message: str):

&quot;&quot;&quot;Simula envio de email.&quot;&quot;&quot;

time.sleep(2) # Simula operação demorada

print(f&quot;Email enviado para {email}: {message}&quot;)

def process_data(data: dict):

&quot;&quot;&quot;Simula processamento pesado.&quot;&quot;&quot;

time.sleep(3)

print(f&quot;Dados processados: {data}&quot;)

@app.post(&quot;/send-notification&quot;)

def send_notification(email: str, background_tasks: BackgroundTasks):

&quot;&quot;&quot;Endpoint que envia notificação e retorna imediatamente.&quot;&quot;&quot;

background_tasks.add_task(send_email, email, &quot;Sua requisição foi recebida!&quot;)

return {&quot;status&quot;: &quot;Notificação será enviada em breve&quot;}

@app.post(&quot;/process&quot;)

def process(data: dict, background_tasks: BackgroundTasks):

&quot;&quot;&quot;Endpoint que inicia processamento em background.&quot;&quot;&quot;

background_tasks.add_task(process_data, data)

return {&quot;status&quot;: &quot;Processamento iniciado&quot;}</code></pre>

<p>Quando você chama <code>POST /send-notification?email=user@example.com</code>, o endpoint retorna imediatamente com status 200, enquanto o envio de email ocorre em segundo plano. O client não espera pelos 2 segundos de simulação.</p>

<h3>Múltiplas Tasks e Dependências</h3>

<p>Você pode adicionar várias tarefas na mesma requisição. Elas serão executadas sequencialmente após a resposta ser enviada:</p>

<pre><code class="language-python">from fastapi import FastAPI, BackgroundTasks, Depends

from datetime import datetime

app = FastAPI()

def log_event(event: str):

&quot;&quot;&quot;Registra um evento.&quot;&quot;&quot;

print(f&quot;[{datetime.now()}] {event}&quot;)

def send_email_task(email: str):

&quot;&quot;&quot;Envia email.&quot;&quot;&quot;

print(f&quot;Email enviado para {email}&quot;)

def update_analytics(action: str):

&quot;&quot;&quot;Atualiza analytics.&quot;&quot;&quot;

print(f&quot;Analytics atualizado: {action}&quot;)

def verify_email(email: str) -&gt; str:

&quot;&quot;&quot;Dependência que valida email.&quot;&quot;&quot;

if &quot;@&quot; not in email:

raise ValueError(&quot;Email inválido&quot;)

return email

@app.post(&quot;/register&quot;)

def register(email: str, background_tasks: BackgroundTasks, validated_email: str = Depends(verify_email)):

&quot;&quot;&quot;Endpoint que usa dependência e múltiplas background tasks.&quot;&quot;&quot;

Adiciona tarefas em ordem

background_tasks.add_task(log_event, f&quot;Novo usuário: {email}&quot;)

background_tasks.add_task(send_email_task, email)

background_tasks.add_task(update_analytics, &quot;registration&quot;)

return {&quot;status&quot;: &quot;Registro iniciado&quot;, &quot;email&quot;: validated_email}</code></pre>

<p>As tarefas são executadas na ordem em que foram adicionadas, sequencialmente.</p>

<h3>Background Tasks com Contexto</h3>

<p>Em aplicações reais, você frequentemente precisa passar contexto ou estado para as tarefas. Uma abordagem elegante é usar classes:</p>

<pre><code class="language-python">from fastapi import FastAPI, BackgroundTasks

from sqlalchemy import create_engine

from sqlalchemy.orm import Session

import time

app = FastAPI()

class TaskProcessor:

&quot;&quot;&quot;Classe que processa tarefas em background com contexto.&quot;&quot;&quot;

def __init__(self, db_connection_string: str):

self.db = create_engine(db_connection_string)

def send_welcome_email(self, user_id: int, email: str):

&quot;&quot;&quot;Envia email de boas-vindas com acesso ao banco.&quot;&quot;&quot;

Simula acesso ao banco para obter template

print(f&quot;Buscando template no banco para usuário {user_id}&quot;)

time.sleep(1)

print(f&quot;Email de boas-vindas enviado para {email}&quot;)

def cleanup_old_sessions(self):

&quot;&quot;&quot;Limpa sessões antigas.&quot;&quot;&quot;

print(&quot;Limpando sessões antigas do banco&quot;)

time.sleep(0.5)

Instância global do processador

task_processor = TaskProcessor(&quot;sqlite:///./test.db&quot;)

@app.post(&quot;/signup&quot;)

def signup(email: str, background_tasks: BackgroundTasks):

&quot;&quot;&quot;Endpoint que usa task processor com contexto.&quot;&quot;&quot;

user_id = 42 # Em produção, viria do banco

background_tasks.add_task(task_processor.send_welcome_email, user_id, email)

background_tasks.add_task(task_processor.cleanup_old_sessions)

return {&quot;status&quot;: &quot;Conta criada&quot;, &quot;user_id&quot;: user_id}</code></pre>

<h2>WebSockets: Comunicação Bidirecional em Tempo Real</h2>

<p>WebSockets permitem comunicação full-duplex entre cliente e servidor em tempo real. Diferentemente de requisições HTTP tradicionais que são unidirecionais, WebSockets mantêm uma conexão aberta onde ambos os lados podem enviar mensagens a qualquer momento. Isso é perfeito para chats, notificações ao vivo, dashboards em tempo real ou qualquer aplicação que precise de atualizações imediatas.</p>

<p>FastAPI, baseado em Starlette, oferece suporte robusto a WebSockets com sintaxe simples. A conexão é mantida viva enquanto ambos os lados mantiverem a conexão aberta, e você pode gerenciar múltiplas conexões simultâneas.</p>

<h3>WebSocket Básico: Echo Server</h3>

<p>Comecemos com o exemplo mais simples: um servidor que retorna tudo que recebe:</p>

<pre><code class="language-python">from fastapi import FastAPI, WebSocket

from starlette.websockets import WebSocketDisconnect

app = FastAPI()

@app.websocket(&quot;/ws&quot;)

async def websocket_endpoint(websocket: WebSocket):

&quot;&quot;&quot;WebSocket básico que ecoa mensagens recebidas.&quot;&quot;&quot;

await websocket.accept()

try:

while True:

Recebe mensagem do cliente

data = await websocket.receive_text()

Envia de volta

await websocket.send_text(f&quot;Você disse: {data}&quot;)

except WebSocketDisconnect:

print(&quot;Cliente desconectado&quot;)

Cliente para testar (em outro arquivo ou terminal)

python -m pip install websockets

python cliente_ws.py</code></pre>

<p>Cliente Python para testar:</p>

<pre><code class="language-python">import asyncio

import websockets

async def test():

async with websockets.connect(&quot;ws://localhost:8000/ws&quot;) as websocket:

Envia mensagem

await websocket.send(&quot;Olá, servidor!&quot;)

Recebe resposta

response = await websocket.recv()

print(f&quot;Resposta: {response}&quot;)

asyncio.run(test())</code></pre>

<p>Quando você conecta, o servidor chama <code>accept()</code> para aceitar a conexão. Depois, entra em um loop esperando mensagens com <code>receive_text()</code>. Quando o cliente desconecta, a exceção <code>WebSocketDisconnect</code> é capturada.</p>

<h3>Chat Multiusuário com Manager de Conexões</h3>

<p>Uma aplicação real requer gerenciar múltiplas conexões ativas. O padrão é usar uma classe <code>ConnectionManager</code>:</p>

<pre><code class="language-python">from fastapi import FastAPI, WebSocket

from starlette.websockets import WebSocketDisconnect

from typing import List

from datetime import datetime

app = FastAPI()

class ConnectionManager:

&quot;&quot;&quot;Gerencia múltiplas conexões WebSocket.&quot;&quot;&quot;

def __init__(self):

self.active_connections: List[WebSocket] = []

async def connect(self, websocket: WebSocket):

&quot;&quot;&quot;Aceita e registra nova conexão.&quot;&quot;&quot;

await websocket.accept()

self.active_connections.append(websocket)

print(f&quot;Cliente conectado. Total: {len(self.active_connections)}&quot;)

async def disconnect(self, websocket: WebSocket):

&quot;&quot;&quot;Remove conexão desconectada.&quot;&quot;&quot;

self.active_connections.remove(websocket)

print(f&quot;Cliente desconectado. Total: {len(self.active_connections)}&quot;)

async def broadcast(self, message: str):

&quot;&quot;&quot;Envia mensagem para todos os clientes conectados.&quot;&quot;&quot;

for connection in self.active_connections:

try:

await connection.send_text(message)

except Exception as e:

print(f&quot;Erro ao enviar para cliente: {e}&quot;)

async def broadcast_json(self, message: dict):

&quot;&quot;&quot;Envia JSON para todos os clientes.&quot;&quot;&quot;

import json

for connection in self.active_connections:

try:

await connection.send_text(json.dumps(message))

except Exception as e:

print(f&quot;Erro ao enviar JSON: {e}&quot;)

manager = ConnectionManager()

@app.websocket(&quot;/ws/chat/{username}&quot;)

async def websocket_chat(websocket: WebSocket, username: str):

&quot;&quot;&quot;WebSocket para chat com broadcast.&quot;&quot;&quot;

await manager.connect(websocket)

try:

Notifica que usuário entrou

await manager.broadcast(f&quot;{username} entrou no chat&quot;)

while True:

Recebe mensagem

data = await websocket.receive_text()

Envia para todos

message = {

&quot;timestamp&quot;: datetime.now().isoformat(),

&quot;username&quot;: username,

&quot;message&quot;: data

}

await manager.broadcast_json(message)

except WebSocketDisconnect:

await manager.disconnect(websocket)

await manager.broadcast(f&quot;{username} saiu do chat&quot;)</code></pre>

<p>Cliente JavaScript para testar:</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;

&lt;html&gt;

&lt;head&gt;

&lt;title&gt;Chat WebSocket&lt;/title&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;input id=&quot;messageInput&quot; type=&quot;text&quot; placeholder=&quot;Digite uma mensagem&quot;&gt;

&lt;button onclick=&quot;sendMessage()&quot;&gt;Enviar&lt;/button&gt;

&lt;ul id=&quot;messages&quot;&gt;&lt;/ul&gt;

&lt;script&gt;

const username = prompt(&quot;Seu nome:&quot;);

const ws = new WebSocket(ws://localhost:8000/ws/chat/${username});

ws.onmessage = (event) =&gt; {

const data = JSON.parse(event.data);

const li = document.createElement(&quot;li&quot;);

li.textContent = ${data.username}: ${data.message};

document.getElementById(&quot;messages&quot;).appendChild(li);

};

function sendMessage() {

const input = document.getElementById(&quot;messageInput&quot;);

ws.send(input.value);

input.value = &quot;&quot;;

}

&lt;/script&gt;

&lt;/body&gt;

&lt;/html&gt;</code></pre>

<p>Este é um chat funcional onde qualquer mensagem enviada por um cliente é broadcast para todos os outros clientes conectados.</p>

<h3>WebSocket com Autenticação e Dados Estruturados</h3>

<p>Em produção, você precisa autenticar WebSocket e trabalhar com dados estruturados. Aqui está um exemplo mais robusto:</p>

<pre><code class="language-python">from fastapi import FastAPI, WebSocket, WebSocketException, status, Query

from pydantic import BaseModel

from starlette.websockets import WebSocketDisconnect

import json

import jwt

from datetime import datetime

app = FastAPI()

class Message(BaseModel):

&quot;&quot;&quot;Modelo para mensagens estruturadas.&quot;&quot;&quot;

action: str

content: str

class AuthenticatedConnectionManager:

&quot;&quot;&quot;Manager com autenticação.&quot;&quot;&quot;

def __init__(self):

self.active_connections: dict = {} # {user_id: websocket}

async def connect(self, websocket: WebSocket, user_id: str):

await websocket.accept()

self.active_connections[user_id] = websocket

async def disconnect(self, user_id: str):

self.active_connections.pop(user_id, None)

async def send_personal_message(self, user_id: str, message: str):

&quot;&quot;&quot;Envia mensagem para um usuário específico.&quot;&quot;&quot;

if user_id in self.active_connections:

await self.active_connections[user_id].send_text(message)

async def broadcast(self, message: dict):

&quot;&quot;&quot;Broadcast com estrutura.&quot;&quot;&quot;

for user_id, connection in self.active_connections.items():

try:

await connection.send_json(message)

except Exception as e:

print(f&quot;Erro ao enviar: {e}&quot;)

manager = AuthenticatedConnectionManager()

def verify_token(token: str) -&gt; str:

&quot;&quot;&quot;Simula verificação de JWT.&quot;&quot;&quot;

try:

Em produção, use jwt.decode() com secret key

if token.startswith(&quot;valid_&quot;):

return token.replace(&quot;valid_&quot;, &quot;&quot;)

raise Exception(&quot;Token inválido&quot;)

except:

return None

@app.websocket(&quot;/ws/notifications&quot;)

async def websocket_notifications(websocket: WebSocket, token: str = Query(...)):

&quot;&quot;&quot;WebSocket com autenticação via query param.&quot;&quot;&quot;

user_id = verify_token(token)

if not user_id:

await websocket.close(code=status.WS_1008_POLICY_VIOLATION)

return

await manager.connect(websocket, user_id)

try:

await manager.broadcast({

&quot;type&quot;: &quot;user_joined&quot;,

&quot;user_id&quot;: user_id,

&quot;timestamp&quot;: datetime.now().isoformat()

})

while True:

data = await websocket.receive_json()

message = Message(**data)

Processa baseado na ação

if message.action == &quot;notify_all&quot;:

await manager.broadcast({

&quot;type&quot;: &quot;notification&quot;,

&quot;from&quot;: user_id,

&quot;content&quot;: message.content

})

elif message.action == &quot;notify_user&quot;:

Em produção, parsearia o destino de message.content

await manager.send_personal_message(

&quot;other_user&quot;,

json.dumps({&quot;type&quot;: &quot;dm&quot;, &quot;from&quot;: user_id, &quot;content&quot;: message.content})

)

except WebSocketDisconnect:

await manager.disconnect(user_id)

await manager.broadcast({

&quot;type&quot;: &quot;user_left&quot;,

&quot;user_id&quot;: user_id

})</code></pre>

<p>Cliente JavaScript com estrutura:</p>

<pre><code class="language-javascript">const token = &quot;valid_user123&quot;;

const ws = new WebSocket(ws://localhost:8000/ws/notifications?token=${token});

ws.onmessage = (event) =&gt; {

const data = JSON.parse(event.data);

console.log(Tipo: ${data.type}, data);

};

function sendNotification() {

ws.send(JSON.stringify({

action: &quot;notify_all&quot;,

content: &quot;Olá a todos!&quot;

}));

}</code></pre>

<h2>Conclusão</h2>

<p>Você dominou quatro pilares essenciais do FastAPI avançado. O sistema de <code>Depends</code> fornece injeção de dependências poderosa e flexível, permitindo código limpo e reutilizável com resolução automática de sub-dependências. Middleware processam requisições globalmente, ideal para logging, autenticação centralizada e transformação de dados em toda a aplicação, funcionando em camadas bem definidas.</p>

<p><code>BackgroundTasks</code> liberam suas rotas para responder imediatamente ao cliente enquanto operações demoradas executam em segundo plano, melhorando drasticamente a experiência do usuário. Por último, WebSockets abrem a porta para comunicação em tempo real, permitindo desde chats até dashboards que se atualizam automaticamente, com suporte robusto a múltiplas conexões e autenticação.</p>

<p>A verdadeira maestria vem de combinar esses elementos: use <code>Depends</code> para injetar um usuário autenticado, aplique <code>Middleware</code> para validação global, lance uma <code>BackgroundTask</code> para limpeza pesada, e mantenha WebSockets abertos para feedback imediato. Comece simples e escale conforme a complexidade da aplicação demande.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://fastapi.tiangolo.com/tutorial/dependencies/" target="_blank" rel="noopener noreferrer">FastAPI Official Documentation - Depends</a></li>

<li><a href="https://fastapi.tiangolo.com/advanced/websockets/" target="_blank" rel="noopener noreferrer">FastAPI Official Documentation - WebSockets</a></li>

<li><a href="https://www.starlette.io/middleware/" target="_blank" rel="noopener noreferrer">Starlette Middleware Documentation</a></li>

<li><a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank" rel="noopener noreferrer">FastAPI Background Tasks</a></li>

<li><a href="https://realpython.com/fastapi-python-web-apis/" target="_blank" rel="noopener noreferrer">Real Python - Building Web APIs with FastAPI</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Python

Como Usar Introdução ao Python: Filosofia, Instalação, pyenv e Primeiro Script em Produção
Como Usar Introdução ao Python: Filosofia, Instalação, pyenv e Primeiro Script em Produção

A Filosofia do Python Python é uma linguagem de programação nascida em 1989,...

O que Todo Dev Deve Saber sobre Laços em Python: for, while, comprehensions e o Protocolo de Iteração
O que Todo Dev Deve Saber sobre Laços em Python: for, while, comprehensions e o Protocolo de Iteração

Entendendo Laços: A Base da Iteração em Python Laços são estruturas fundament...

Testes de Integração em Python: Banco Real com pytest e Docker na Prática
Testes de Integração em Python: Banco Real com pytest e Docker na Prática

Entendendo Testes de Integração: O que são e por que importam Testes de integ...