Project: MongoDB + Redis + API Deploy

Introduction

This capstone wires MongoDB (source of truth), Redis (cache-aside), and a FastAPI service in Docker Compose. Reads hit Redis when warm; writes update MongoDB and invalidate cache. Health checks prove the stack is ready before traffic arrives.

Prerequisites

Architecture

text
Client → FastAPI (api) → Redis (cache)
              ↓ miss
           MongoDB (mongo)

Write path: MongoDB first, then DEL cache key.

docker-compose.yml

Environment Settings

app/core/config.py:

python
class Settings(BaseSettings):
    mongodb_uri: str = "mongodb://127.0.0.1:27017"
    mongodb_db_name: str = "hello_demo"
    redis_url: str = "redis://127.0.0.1:6379/0"

.env.example (commit this, not .env):

env
MONGODB_URI=mongodb://127.0.0.1:27017
MONGODB_DB_NAME=hello_demo
REDIS_URL=redis://127.0.0.1:6379/0

Warning

Do not commit .env files with production credentials.

Lifespan: Mongo + Redis

Health Check

python
from fastapi import APIRouter, Request
from app.db.mongo import get_database
 
router = APIRouter()
 
@router.get("/health")
def health(request: Request):
    get_database().command("ping")
    request.app.state.redis.ping()
    return {"status": "ok"}

Kubernetes/load balancers call /health before routing.

Cache-Aside Read

TTL 300 seconds—tune per SLA.

Write + Invalidate

python
@router.patch("/posts/{post_id}")
def update_post(post_id: str, body: dict, request: Request):
    if not ObjectId.is_valid(post_id):
        raise HTTPException(status_code=400, detail="Invalid id")
    result = get_posts_collection().update_one(
        {"_id": ObjectId(post_id)},
        {"$set": body},
    )
    if result.matched_count == 0:
        raise HTTPException(status_code=404, detail="Not found")
    request.app.state.redis.delete(f"post:{post_id}")
    return {"updated": True}

Also delete post:slug:{slug} if you cache by slug.

Dockerfile (Minimal)

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

requirements.txt: fastapi, uvicorn, pymongo[srv], redis, pydantic-settings.

Run

bash
docker compose up --build
curl -s http://127.0.0.1:8000/health
curl -s http://127.0.0.1:8000/posts/507f1f77bcf86cd799439011

Second request should be faster (Redis hit)—verify with redis-cli MONITOR locally.

Production Notes

TopicAction
SecretsDocker secrets or env from orchestrator
MongoDB authCreate app user — Security
TLSAtlas or reverse proxy — Nginx HTTPS
Replica setRequired for transactions; recommended for prod — Replica Sets
CIpytest + compose in GitHub Actions — Git CI

Post-Project Checklist

  • docker compose up passes health checks
  • Cache hit on repeated GET
  • PATCH invalidates cache
  • MongoDB data survives docker compose down (volume)

FAQ

Cache stampede?

Use short TTL + single-flight lock in Redis — Redis patterns.

Motor async API?

Swap PyMongo for Motor — chapter 21; keep same Redis pattern.

Write-through instead?

Update Redis and MongoDB in one handler—simpler reads, riskier inconsistency on partial failure.

Atlas + ElastiCache?

Same code; change URIs and security groups — Atlas.

Nginx in front?

Terminate TLS at Nginx, proxy to api:8000FastAPI deploy.