PyMongo With FastAPI
Introduction
FastAPI excels at JSON APIs; MongoDB stores flexible documents— a natural pair for content, catalogs, and event APIs. This chapter wires PyMongo through lifespan, dependency injection, Pydantic schemas, and a small posts CRUD router—following patterns from FastAPI project structure and PyMongo basics.
Prerequisites
- PyMongo Basics
- FastAPI Settings —
get_settings - FastAPI Dependency Injection
- MongoDB URI in
.env
Project Layout
requirements.txt:
fastapi[standard]>=0.115.0
pymongo[srv]>=4.6
pydantic-settings>=2.0
python-dotenv>=1.0Settings
app/core/config.py:
.env:
MONGODB_URI=mongodb://127.0.0.1:27017
MONGODB_DB_NAME=hello_demoAtlas:
MONGODB_URI=mongodb+srv://app_user:PASSWORD@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majorityMongoClient Lifespan
app/db/mongo.py:
app/main.py:
Code explanation:
init_mongo/close_mongomirror Redis lifespanpingon startup fails fast if DB is down
Pydantic Schemas
app/schemas/post.py:
Document ↔ Schema Helpers
app/api/v1/endpoints/posts.py (top):
Ensure index once at startup (optional in init_mongo):
get_posts_collection().create_index("slug", unique=True)CRUD Endpoints
Code explanation:
Depends(get_collection)injects the same collection per request- Pydantic validates input—prevents NoSQL injection via typed fields — Security
409on duplicateslugfrom unique index
Run and Test
uvicorn app.main:app --reloadcurl -s http://127.0.0.1:8000/health
curl -s -X POST http://127.0.0.1:8000/api/v1/posts/ \
-H "Content-Type: application/json" \
-d '{"title":"API Post","slug":"api-post","published":true,"body":"Hello"}'
curl -s "http://127.0.0.1:8000/api/v1/posts/?published=true"Open /docs for Swagger UI — OpenAPI chapter.
MongoDB + SQLAlchemy Split (Architecture Note)
Some products use MySQL/PostgreSQL for billing (ACID, reports) and MongoDB for content or logs. Two connections in lifespan:
# Concept only — two resources in lifespan
init_mongo()
init_sqlalchemy_engine()Do not duplicate transactional data in both without a sync strategy.
Optional: Redis Cache Layer
After get_post, add Redis cache-aside — Performance and FastAPI Redis.
Testing With TestClient
Use mongomock or a disposable Docker MongoDB in CI:
# pytest fixture sketch — override get_collection to test DB
@pytest.fixture
def client():
os.environ["MONGODB_DB_NAME"] = "hello_demo_test"
with TestClient(app) as c:
yield c
get_database().client.drop_database("hello_demo_test")See FastAPI testing.
Post-Chapter Checklist
-
MongoClientinitialized in lifespan - Posts CRUD works via
curlor/docs -
ObjectIdexposed as string in JSON - Unique
slugindex returns 409 on conflict
FAQ
Async routes with PyMongo?
Blocking PyMongo in async def blocks the event loop—use def routes or Motor — chapter 21.
Beanie / ODM?
Beanie adds async document models; this track stays driver-level for clarity.
JWT auth on writes?
Add Depends(get_current_user) from FastAPI JWT.
CORS for SPA?
CORSMiddleware — FastAPI middleware.
Deploy?
Docker Compose with mongo + api — Project deploy.