Two years of production FastAPI + async SQLAlchemy. Here's the real deal, not the tutorial fantasy land where everything works first try.
What You're Actually Getting Into
FastAPI is actually fast, not marketing fast. The OpenAPI docs work great until you have nested Pydantic models with circular dependencies, then they shit the bed. But the performance claims are real - we killed a Django API doing 400 req/sec and replaced it with FastAPI hitting around 2,100 req/sec on the same hardware. The benchmarks aren't lying for once.
SQLAlchemy 2.0 fixed a lot of the shit that was broken in 1.4. The async support actually works now, but migrating from sync code will make you question your career choices. The new query syntax is cleaner but everything you knew is deprecated. Spent weeks reading the migration guide when upgrading our first production app.
Alembic generates migrations that usually work. The keyword is "usually." When it fucks up (and it will), you'll be writing raw SQL at 2 AM trying to fix your production database. But when it works, it's magic. Alembic docs are actually readable, which is rare for database tooling.
PostgreSQL is rock solid. Use version 16+ because the performance improvements are real. PostgreSQL 17 dropped in September 2024 with better JSON performance and parallel query improvements. JSON support is excellent, full-text search doesn't suck, and it scales better than you'll ever need.
The Reality of Async Everything
Async is incredibly fast when you get it right and an absolute clusterfuck when you don't. Learned this debugging why our API was pinning CPU at 100% - turns out one sync database call in an async endpoint blocks the entire event loop. Found a Stack Overflow thread that explained the gotchas - why FastAPI runs serial instead of parallel when you mix sync/async wrong.
SQLAlchemy 2.0's async engine actually works with FastAPI now. No more thread pool exhaustion bullshit. But debugging async database issues feels like debugging race conditions while drunk - you think you know what's happening, but you're probably wrong.
Migration Hell and How to Survive It
Alembic auto-generates migrations from your model changes. Sometimes it works perfectly. Sometimes it tries to drop your entire production table because you changed a field name.
Pro tip from 50+ production migrations: Always review the generated migration before running it. I've seen migrations that would have deleted customer data because Alembic thought a renamed column was a drop + add operation.
TestDriven.io has a decent migration walkthrough, but they skip the fun part where your migration works perfectly on your MacBook and pukes spectacularly on production Ubuntu.
Performance: What's Real vs Marketing
FastAPI's benchmarks claim ridiculous numbers. In practice, on our production hardware (8-core AWS instances), we see somewhere between 3-4k requests/second for database-backed APIs, depending on query complexity. That's still damn good - 10x better than our old Django setup.
The async database sessions prevent the connection pool exhaustion that killed our old sync setup. Connection pooling with asyncpg is solid - we run 20 connections max and handle thousands of concurrent users.
Docker and Deployment Reality
This stack containerizes beautifully. Docker-compose runs the whole thing cleanly. Alembic migrations run automatically on startup - usually. Sometimes they hang and you're SSH'ing into production containers like a caveman.
The type annotations help catch bugs before deployment, but they won't save you from logic errors or async session leaks. SQLAlchemy 2.0's improved typing is better than 1.4, but still not as good as the marketing claims.