跪拜 Guibai
← All articles
Backend · Python

The FastAPI–MySQL Stack That Survives Past the Demo

By copyer_xyf ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Most FastAPI tutorials stop at a single-file demo with a raw connection string. This lays out the layered architecture — Engine, Session factory, repository, service, router — that prevents the database code from becoming unmaintainable spaghetti once the project grows beyond a few endpoints.

Summary

Connecting FastAPI to MySQL means coordinating four distinct layers: PyMySQL handles the wire protocol, SQLAlchemy manages the ORM and connection pool, a per-request Session factory enforces transaction boundaries, and FastAPI's Depends injects that session into route handlers. The article walks through each layer's exact responsibility, from the Engine and SessionLocal configuration to the commit/rollback lifecycle.

A full project structure is laid out with config, database, dependencies, and per-module directories for models, schemas, repository, service, and router files. The repository handles raw reads and writes, the service owns business rules and transaction commits, and the router stays thin — translating HTTP semantics and delegating everything else.

Common pitfalls like forgetting to commit, failing to rollback on exceptions, creating engines inside route handlers, and missing model imports are addressed directly. The guide also clarifies when to use synchronous SQLAlchemy versus the async stack, and why Alembic migrations replace create_all in team projects.

Takeaways
PyMySQL is the actual MySQL driver; SQLAlchemy sits above it as the ORM and connection-pool manager.
Engine is created once at startup and holds the connection pool; SessionLocal is a factory that produces a new Session per request.
Setting autoflush=False, autocommit=False, and expire_on_commit=False on the session factory gives explicit control over when writes and flushes happen.
Every write operation must be followed by db.commit(); data is not persisted until the transaction commits.
Wrap multi-step writes in try/except with db.rollback() in the except block, or use SessionLocal.begin() for automatic rollback.
Use FastAPI's Depends with a generator function to create and close a database session per request, returning the connection to the pool in a finally block.
A maintainable project separates config, database infrastructure, dependencies, and per-module models/schemas/repository/service/router files.
Repository classes handle raw database reads and writes; Service classes own business rules and transaction boundaries; routers stay thin and only handle HTTP concerns.
Import all ORM models centrally (e.g., app/db/models.py) before calling create_all or running Alembic, or tables will be silently missing.
Start with synchronous SQLAlchemy + PyMySQL; only adopt the async stack (create_async_engine, asyncmy/aiomysql, AsyncSession) when the project genuinely needs it.
Conclusions

The guide's emphasis on explicit driver naming — mysql+pymysql:// rather than mysql:// — is a small habit that prevents environment-specific breakage in team settings.

Placing commit and rollback inside the Service layer, not the Repository, keeps transaction boundaries aligned with business actions rather than individual database calls.

The distinction between Session (a unit-of-work context) and the web notion of a session is a persistent source of confusion for newcomers; the article addresses it head-on.

Recommending synchronous SQLAlchemy as the default, with async treated as an optimization for specific needs, pushes back against the reflex to make everything async just because FastAPI supports it.

Concepts & terms
Engine (SQLAlchemy)
The application-level database entry point that holds the connection pool. Created once at startup and reused across all requests.
Session (SQLAlchemy)
A per-request unit-of-work context that tracks queried and modified objects, and controls commit and rollback. Not related to HTTP sessions.
SessionLocal
A sessionmaker factory configured with an Engine. Called once per request to produce a new SQLAlchemy Session.
DBAPI
A Python specification for database driver interfaces. PyMySQL implements DBAPI so SQLAlchemy can communicate with MySQL through it.
pool_pre_ping
A SQLAlchemy Engine parameter that tests a connection's liveness before handing it out from the pool, preventing errors from MySQL's idle-connection timeouts.
From the discussion
Featured comments
爬虫技术实验室

About FastAPI connecting to databases, I also started by writing the synchronous version following the docs. Later, when building a proxy gateway, I discovered a big pitfall 😂 The routing layer used async, but the database operations still used synchronous SQLAlchemy sessions, meaning every DB query was blocking the event loop. After switching everything to async session + aiomysql, with the same concurrency, the response time dropped by almost half. But the dependency injection pattern mentioned in the article is indeed the most comfortable — each request gets its own independent session, automatically closed when the request ends, much cleaner than manually managing sessions back in my Flask days.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗