API versioning strategies including URL path, header, and content negotiation. Use when migrating v1 to v2, handling breaking changes, implementing deprecation or sunset policies, or managing backward compatibility.
View on GitHubyonatangross/orchestkit
ork
January 25, 2026
Select agents to install to:
npx add-skill https://github.com/yonatangross/orchestkit/blob/main/plugins/ork/skills/api-versioning/SKILL.md -a claude-code --skill api-versioningInstallation paths:
.claude/skills/api-versioning/# API Versioning Strategies
Design APIs that evolve gracefully without breaking clients.
## Strategy Comparison
| Strategy | Example | Pros | Cons |
|----------|---------|------|------|
| URL Path | `/api/v1/users` | Simple, visible, cacheable | URL pollution |
| Header | `X-API-Version: 1` | Clean URLs | Hidden, harder to test |
| Query Param | `?version=1` | Easy testing | Messy, cache issues |
| Content-Type | `Accept: application/vnd.api.v1+json` | RESTful | Complex |
## URL Path Versioning (Recommended)
### FastAPI Structure
```
backend/app/
├── api/
│ ├── v1/
│ │ ├── __init__.py
│ │ ├── routes/
│ │ │ ├── users.py
│ │ │ └── analyses.py
│ │ └── router.py
│ ├── v2/
│ │ ├── __init__.py
│ │ ├── routes/
│ │ │ ├── users.py # Updated schemas
│ │ │ └── analyses.py
│ │ └── router.py
│ └── router.py # Combines all versions
```
### Router Setup
```python
# backend/app/api/router.py
from fastapi import APIRouter
from app.api.v1.router import router as v1_router
from app.api.v2.router import router as v2_router
api_router = APIRouter()
api_router.include_router(v1_router, prefix="/v1")
api_router.include_router(v2_router, prefix="/v2")
# main.py
app.include_router(api_router, prefix="/api")
```
### Version-Specific Schemas
```python
# v1/schemas/user.py
class UserResponseV1(BaseModel):
id: str
name: str # Single name field
# v2/schemas/user.py
class UserResponseV2(BaseModel):
id: str
first_name: str # Split into first/last
last_name: str
full_name: str # Computed for convenience
```
### Shared Business Logic
```python
# services/user_service.py (version-agnostic)
class UserService:
async def get_user(self, user_id: str) -> User:
return await self.repo.get_by_id(user_id)
# v1/routes/users.py
@router.get("/{user_id}", response_model=UserResponseV1)
async def get_user_v1(user_id: str, service: UserService = Depends()):
user = await service.get_user