Como expor uma API de Dados em read-only com FastAPI: passo a passo
Este tutorial mostra como expor uma API de Dados em modo read-only usando FastAPI para servir consultas a uma base de dados SQLite. É útil para partilhar dados de forma segura e eficiente, com paginação e proteção básica contra uso indevido.
Pré-requisitos
- Python 3.10+ instalado
- Conhecimentos básicos de Python e SQL
- Packs: fastapi, uvicorn, sqlalchemy, pydantic (pip install fastapi uvicorn sqlalchemy pydantic)
Passo 1: Estrutura mínima e porquê do read-only
Uma API read-only evita alterações acidentais nos dados e simplifica autenticação/controlo. Vamos criar a estrutura mínima com FastAPI e SQLAlchemy para consultar uma tabela "items".
project/
app.py
models.py
database.db # SQLite de exemplo
Passo 2: Definir o modelo SQLAlchemy e criar dados de exemplo
Criamos um modelo simples para a tabela items com id, name e price. Usamos SQLite para portabilidade.
# models.py
from sqlalchemy import Column, Integer, String, Float, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
price = Column(Float, nullable=False)
# Criar DB e adicionar dados de exemplo (executar uma vez)
if __name__ == '__main__':
engine = create_engine('sqlite:///database.db')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
s = Session()
s.add_all([
Item(name='Caneta', price=1.2),
Item(name='Caderno', price=3.5),
Item(name='Mochila', price=25.0)
])
s.commit()
s.close()
Passo 3: Criar a API read-only com FastAPI
Vamos expor dois endpoints: listar itens com paginação e obter um item por id. Usamos Pydantic para o esquema de resposta e garantimos que não há rotas que alterem dados.
# app.py
from fastapi import FastAPI, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Item, Base
DATABASE_URL = 'sqlite:///database.db'
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine)
app = FastAPI(title='Items API Read-Only')
class ItemOut(BaseModel):
id: int
name: str
price: float
class Config:
orm_mode = True
# Dependência para obter sessão
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get('/items', response_model=list[ItemOut])
def list_items(page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), db=Depends(get_db)):
offset = (page - 1) * page_size
items = db.query(Item).offset(offset).limit(page_size).all()
return items
@app.get('/items/{item_id}', response_model=ItemOut)
def get_item(item_id: int, db=Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail='Item não encontrado')
return item
Passo 4: Adicionar autenticação simples (API Key) e porquê
Mesmo em read-only, convém controlar quem acede. Vamos usar um header X-API-Key e validar com uma dependência. Em produção, usa um sistema de identidade.
from fastapi import Header
API_KEY = 'minha_chave_exemplo' # em produção não hardcode!
def verify_api_key(x_api_key: str = Header(...)):
if x_api_key != API_KEY:
raise HTTPException(status_code=401, detail='API Key inválida')
# aplicar verify_api_key como dependency global (exemplo)
app.dependencies.append(Depends(verify_api_key))
Passo 5: Lidar com erros comuns e práticas recomendadas
Erros comuns: esquecer connect_args no SQLite; não fechar sessões; expor rotas de escrita por engano. Recomenda-se limitar page_size, validar parâmetros e registar acessos para auditoria.
# Exemplo simples de logging de consulta
import logging
logging.basicConfig(level=logging.INFO)
@app.middleware('http')
async def log_requests(request, call_next):
logging.info(f'Pedido {request.method} {request.url}')
response = await call_next(request)
return response
Verificar o resultado
Executa a API com uvicorn e testa com curl ou um browser. Deverás obter listas paginadas e detalhes por id, e receber 401 sem API Key.
# Executar
uvicorn app:app --reload --port 8000
# Testes
curl -H "X-API-Key: minha_chave_exemplo" "http://localhost:8000/items?page=1&page_size=2"
curl -H "X-API-Key: minha_chave_exemplo" "http://localhost:8000/items/1"
Conclusão
Acabaste de criar uma API de Dados read-only com FastAPI, SQLite e autenticação por API Key, com paginação e logging básicos. Próximos passos: trocar SQLite por uma base de dados gerida, adicionar testes automatizados e usar OAuth2 ou JWT para autenticação. Dica: que métricas vais registar para monitorizar uso da API?