Añadir Redis a FastAPI con Docker y optimizar con cache

Tiempo de lectura: 2 minutos

Stack: FastAPI · Docker Compose · fastapi-cache2 · Redis 7

Barco en el mar - pexels

¿Por qué Redis?

Sin caché, cada petición va a la base de datos aunque los datos no hayan cambiado. Con Redis, la primera llamada consulta la BD y guarda el resultado. Las siguientes peticiones devuelven Redis directamente, sin tocar la BD.

EscenarioSin RedisCon Redis
GET /libros (1ª vez)~80ms (BD)~80ms (BD)
GET /libros (2ª vez)~80ms (BD)~2ms (caché)
GET /categorias x 1008.000ms total~2ms total

1. Docker Compose

Añade el servicio Redis y actualiza tu API para que dependa de él.

Añade el servicio Redis

redis_bb:
  image: redis:7-alpine
  restart: unless-stopped
  container_name: redis_bb
  networks:
    - docker-network

Actualiza el servicio de tu API

bb_back_api:
  ...
  depends_on:
    mariadb:
      condition: service_healthy
    redis_bb:           # ← añade esto
      condition: service_started

Levanta los contenedores

docker compose up -d

Verifica que Redis está corriendo:

docker ps | grep redis

2. Dependencias Python

Añade a requirements.txt:

redis==5.2.1
fastapi-cache2==0.2.2

Reconstruye el contenedor:

docker compose build
docker compose up -d

3. Configuración en main.py

Inicializa la conexión a Redis al arrancar la app:

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from redis import asyncio as aioredis

app = FastAPI(
    title="API",
    description="API REST",
    default_response_class=ORJSONResponse,
)

@app.on_event("startup")
async def startup():
    redis = aioredis.from_url("redis://redis_bb:6379")
    FastAPICache.init(RedisBackend(redis), prefix="bb-cache")

Nota: El nombre del host redis_qbb es el nombre del servicio en docker-compose.yml, no localhost.

4. Cachear endpoints

Caso A — TTL corto (más simple)

Ideal para datos que cambian poco. La caché expira sola, sin código extra.

from fastapi_cache.decorator import cache

@app.get("/categorias")
@cache(expire=300)  # refresca cada 5 minutos
async def get_categorias():
    return await db.fetch_all("SELECT * FROM categorias")

Caso B — Invalidación manual (más preciso)

Ideal para datos que cambian y necesitas que se refleje al instante.

Marca el endpoint con un namespace:

@app.get("/libros")
@cache(expire=3600, namespace="libros")  # 1 hora, pero se puede limpiar
async def get_libros():
    return await db.fetch_all("SELECT * FROM libros")

Limpia la caché cuando añaden o editan un libro:

from fastapi_cache import FastAPICache

@app.post("/libros")
async def crear_libro(libro: LibroSchema):
    await db.execute(insert_query, libro.dict())

    # Invalida la caché al modificar datos
    await FastAPICache.clear(namespace="libros")

    return {"status": "ok"}

5. ¿Qué estrategia usar?

SituaciónEstrategia recomendada
Categorías, géneros, editorialesTTL largo (1 hora+)
Listado de libros generalTTL corto (1-5 min) o invalidación manual
Búsquedas y filtrosTTL corto (30-60 seg)
Perfil de usuarioSin caché (datos personales)
Datos en tiempo realSin caché

6. Verificar que funciona

Haz dos peticiones al mismo endpoint y compara los tiempos de respuesta en los logs. La segunda debe ser notablemente más rápida.

También puedes conectarte a Redis y ver las claves guardadas:

docker exec -it redis_bb redis-cli
> KEYS *
> TTL bb-cache:libros

Deja un comentario