Technical
API Versioning: Why You Should Plan for It from Day One
I shipped an API without versioning. Six months later, I needed to change the response format. Every frontend that consumed the API broke simultaneously. That is a mistake you only make once. Here is how to version your API from the start.
Why Versioning Matters
APIs are contracts. When a frontend consumes your API, it depends on the response shape, the field names, the status codes, and the error format. Changing any of these is a breaking change that requires every consumer to update simultaneously.
Versioning lets you introduce breaking changes in a new version while keeping the old version running. Consumers migrate at their own pace.
The Three Approaches
1. URL Path Versioning (My Recommendation)
GET /api/v1/posts
GET /api/v2/postsPros: Visible, easy to understand, easy to route. Cons: URL changes between versions.
2. Header Versioning
GET /api/posts
Accept: application/vnd.peaklight.v2+jsonPros: Clean URLs that do not change. Cons: Hidden, easy to forget, harder to test.
3. Query Parameter Versioning
GET /api/posts?version=2Pros: Easy to add to existing APIs. Cons: Feels hacky, clutters URLs.
Implementing in FastAPI
URL path versioning with FastAPI routers:
from fastapi import APIRouter
v1_router = APIRouter(prefix='/api/v1')
v2_router = APIRouter(prefix='/api/v2')
@v1_router.get('/posts')
async def list_posts_v1():
# Returns old format: {posts: [...]}
pass
@v2_router.get('/posts')
async def list_posts_v2():
# Returns new format: {data: [...], meta: {...}}
pass
app.include_router(v1_router)
app.include_router(v2_router)When to Create a New Version
Create a new version when you make a breaking change:
- Removing or renaming a field
- Changing a field's type (string to number)
- Changing the response structure
- Removing an endpoint
Do NOT create a new version for:
- Adding new fields (backward compatible)
- Adding new endpoints (backward compatible)
- Fixing bugs in existing behavior
- Performance improvements
The Deprecation Process
When you release v2:
- Keep v1 running and unchanged
- Add a deprecation header to v1 responses:
Deprecation: true - Document the migration path from v1 to v2
- Give consumers a reasonable timeline (3-6 months)
- Monitor v1 usage and reach out to remaining consumers
- Turn off v1 after the deadline
Start Simple
Even if you do not need versioning today, prefix your routes with /api/v1/. It costs nothing and saves you from a painful migration when you eventually need v2. Five minutes of planning today prevents weeks of pain later.
See the FastAPI router documentation for implementing versioned routers.
RELATED READING
The Consulting Shift I Am Making In Year Two
After a year of writing and building, my consulting practice is changing shape. Shorter engagements. Sharper outcomes.
ReadThe Frontend Shift: Shipping Less JavaScript In Year Two
A year ago I reached for Next.js for everything. This year I often reach for nothing.
ReadThe Serverless Lesson I Would Write On A Sticky Note
After a year of shipping serverless projects, one rule explains most of the wins and all of the losses.
Read