(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Cómo exponer una API de Datos en read-only con FastAPI: paso a paso

João Barros 23 de September de 2026 4 min de lectura

Este tutorial muestra cómo exponer una API de Datos en modo read-only usando FastAPI para servir consultas a una base de datos SQLite. Es útil para compartir datos de forma segura y eficiente, con paginación y protección básica contra uso indebido.

Requisitos previos

  • Python 3.10+ instalado
  • Conocimientos básicos de Python y SQL
  • Packs: fastapi, uvicorn, sqlalchemy, pydantic (pip install fastapi uvicorn sqlalchemy pydantic)

Paso 1: Estructura mínima y porqué del read-only

Una API read-only evita modificaciones accidentales en los datos y simplifica la autenticación/control. Vamos a crear la estructura mínima con FastAPI y SQLAlchemy para consultar una tabla "items".

project/
  app.py
  models.py
  database.db  # SQLite de ejemplo

Paso 2: Definir el modelo SQLAlchemy y crear datos de ejemplo

Creamos un modelo simple para la tabla items con id, name y price. Usamos SQLite por portabilidad.

# 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)

# Crear DB y añadir datos de ejemplo (ejecutar una 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()

Paso 3: Crear la API read-only con FastAPI

Vamos a exponer dos endpoints: listar ítems con paginación y obtener un ítem por id. Usamos Pydantic para el esquema de respuesta y garantizamos que no hay rutas que modifiquen datos.

# 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

# Dependencia para obtener sesión
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

Paso 4: Añadir autenticación simple (API Key) y porqué

Incluso en read-only, conviene controlar quién accede. Vamos a usar un header X-API-Key y validar con una dependencia. En producción, usa un sistema de identidad.

from fastapi import Header

API_KEY = 'minha_chave_exemplo'  # en producción no hardcodear!

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 (ejemplo)
app.dependencies.append(Depends(verify_api_key))

Paso 5: Manejar errores comunes y buenas prácticas

Errores comunes: olvidar connect_args en SQLite; no cerrar sesiones; exponer rutas de escritura por error. Se recomienda limitar page_size, validar parámetros y registrar accesos para auditoría.

# Ejemplo simple 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 el resultado

Ejecuta la API con uvicorn y prueba con curl o un navegador. Deberías obtener listas paginadas y detalles por id, y recibir 401 sin API Key.

# Ejecutar
uvicorn app:app --reload --port 8000

# Pruebas
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"

Conclusión

Acabas de crear una API de Datos read-only con FastAPI, SQLite y autenticación por API Key, con paginación y logging básicos. Próximos pasos: cambiar SQLite por una base de datos gestionada, añadir pruebas automatizadas y usar OAuth2 o JWT para la autenticación. Consejo: ¿qué métricas vas a registrar para monitorizar el uso de la API?