I'm managing a project that requires supporting both v1 and v2 of our API simultaneously. I'm using FastAPI APIRouter. What is the cleanest way to handle versioning so that common logic isn't duplicated across folders, but I can still have distinct schemas for each version?
3 answers
The most maintainable approach in FastAPI is to use directory-based versioning for your routers (e.g., api/v1/endpoints/ and api/v2/endpoints/). For the logic, move your "business logic" into a separate services/ layer that both versions call. This way, the routers only handle the Pydantic schema validation and status codes. If v2 requires a new field, you just update the v2 Pydantic model and the service function, while v1 remains tied to the old model. This keeps your code DRY while allowing the public contract to evolve.
What about header-based versioning? Is it possible to use FastAPI dependencies to route a request to a specific function based on the Accept-Version header instead of using URL prefixes?
I always use the URL prefix method. It makes it very clear in the logs which version of the API is being hit the most.
Good point, Theresa. Observability is a huge factor when deciding on an architecture for long-term projects.
It is possible but much harder to document with Swagger/OpenAPI. FastAPI's automatic docs work best with URL prefixes (/v1, /v2). If you use headers, you often have to write a custom OpenAPI generator script to show all versions clearly to your frontend team.