FastAPI: When "Modern" Features Meet Reality
Date: 2024-03-19 Tags: #python #fastapi #technicaldebt #overengineering #whyisthissohard Author: Mercury TA Team
Dear FastAPI maintainers,
I've just spent a week in dependency injection hell, fighting with your framework's response handling that somehow manages to break basic HTTP protocol. Let me share my experience with this overengineered nightmare.
The Promise vs Reality
You promised:
- "FastAPI is modern, fast (high-performance)"
- "One of the fastest Python frameworks available"
- "Fewer bugs"
- "Easy to use"
What we got:
- A framework that can't handle Content-Length headers properly
- Mysterious H11 protocol errors that appear randomly
- Response handling that makes Redis cache operations feel like quantum physics
- Stack traces longer than my career in software development
The "Features" That Keep on Giving
-
Dependency Injection Why use simple function calls when you can create a dependency injection system so complex it makes Spring Boot blush? Thanks for making
async def depends_on_depends_on_depends_on_what_the_actual_f()a thing. -
Type Hints Because nothing says "modern Python" like turning every function into a Java-esque type carnival:
async def my_route(
response: Response = Response,
depends: Annotated[Dict[str, Any], Depends(dependency)],
another_depends: Annotated[Optional[List[Dict[str, Any]]], Depends(another_dependency)]
) -> Union[JSONResponse, Response, None]:
# Good luck figuring out what this actually returns
pass -
Response Handling Q: How many frameworks does it take to handle a simple JSON response? A: Just one FastAPI, but it'll involve Starlette, Pydantic, and somehow still manage to mess up the Content-Length header.
The HTTP Protocol: A Suggestion, Not a Requirement
Your framework treats HTTP protocol compliance like it's optional. Content-Length? More like Content-Maybe.
When I get LocalProtocolError: Too much data for declared Content-Length, is it:
a) My middleware?
b) Your response handling?
c) A temporal anomaly in the space-time continuum?
d) All of the above, randomly, depending on the phase of the moon?
Real World Usage
You know what's fun? When your production service starts throwing:
h11._util.RemoteProtocolError: Got data when expecting EOF
LocalProtocolError: Can't send data when our state is ERROR
And your logs look like a stack trace modern art exhibition.
The Migration Story
After battling with FastAPI's "features", we decided to move to a more sensible framework. The requirements were simple:
- Must handle HTTP protocol correctly (shocking, I know)
- Should not require a PhD in type theory
- Responses should actually contain the amount of data they claim to have
We ended up choosing [redacted] because sometimes boring technology is exactly what you need when you just want your API to work.
In Conclusion
FastAPI is what happens when you try to make Python look like TypeScript while handling responses like PHP and managing dependencies like Java. It's a framework that turns simple HTTP responses into a distributed systems problem.
I'm moving to Flask/aiohttp where at least when something breaks, it has the courtesy to break in a way that makes sense.
P.S. Your error messages are longer than the actual code that caused them. That's not documentation, that's a cry for help.
Lessons Learned
- Just because a framework is popular doesn't mean it's good
- Type hints are great until they're not
- Async is powerful until your framework makes it unpredictable
- Sometimes the old, boring solution is the right solution
- HTTP is a solved problem - unless you're using FastAPI
Sincerely, A Developer Who Just Wanted to Return Some JSON
Note: This rant was inspired by real events. No HTTP protocols were successfully implemented during the making of this post.