URL, 헤더 및 미디어 유형 버전 관리
버전 관리 방식을 비교하고 깔끔한 경로 그룹을 구현해 클라이언트가 중단 없이 업그레이드하도록 합니다.
URL, 헤더 및 미디어 유형 버전 관리은(는) CoddyKit의 무료 FastAPI Backend Development Bootcamp 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 FastAPI Backend Development Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. FastAPI Backend Development Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why Version an API at All?
Once real clients depend on your API, you can no longer change a response shape freely. Renaming a field, removing a key, or changing a status code can break production apps you do not control.
Versioning lets you ship breaking changes under a new label while the old behavior keeps working. Clients upgrade on their own schedule.
- Non-breaking change (add an optional field) usually needs no new version.
- Breaking change (remove/rename a field, change types, change semantics) needs a version boundary.
This lesson compares the three classic strategies: URL path, Header, and Media-Type versioning.
Strategy 1: URL Path Versioning
The most common and most visible approach: put the version directly in the path, e.g. /v1/users and /v2/users.
- Pros: obvious in logs and browsers, trivial to route, easy to cache, simple to document.
- Cons: the version leaks into every URL; technically a URL should identify a resource, not a representation.
In FastAPI you express this with a prefix on an APIRouter. Each version gets its own router and its own prefix.
from fastapi import APIRouter, FastAPI
app = FastAPI()
v1 = APIRouter(prefix="/v1", tags=["v1"])
v2 = APIRouter(prefix="/v2", tags=["v2"])
@v1.get("/users/{user_id}")
def get_user_v1(user_id: int):
return {"id": user_id, "name": "Ada Lovelace"}
@v2.get("/users/{user_id}")
def get_user_v2(user_id: int):
# v2 splits name into first/last
return {"id": user_id, "first_name": "Ada", "last_name": "Lovelace"}
app.include_router(v1)
app.include_router(v2)Clean Route Grouping with Sub-Routers
Do not stack every endpoint onto one giant version router. Group by resource, then mount each resource router under the version router so structure stays clean as the API grows.
Here users_router is built independently and then included into v1. The combined path becomes /v1/users/....
from fastapi import APIRouter, FastAPI
app = FastAPI()
users_router = APIRouter(prefix="/users", tags=["users"])
@users_router.get("/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "Grace Hopper"}
v1 = APIRouter(prefix="/v1")
v1.include_router(users_router) # -> /v1/users/{user_id}
app.include_router(v1)Strategy 2: Header Versioning
Here the URL stays clean (/users/42) and the client signals the version through a custom request header, commonly X-API-Version: 2.
- Pros: URLs are version-free and stable; the resource path never changes.
- Cons: harder to test in a browser, easy to forget the header, and caching layers must be told to vary on it.
In FastAPI you read the header with a typed parameter and branch (or dispatch) on it.
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/users/{user_id}")
def get_user(user_id: int, x_api_version: int = Header(default=1)):
if x_api_version >= 2:
return {"id": user_id, "first_name": "Ada", "last_name": "Lovelace"}
return {"id": user_id, "name": "Ada Lovelace"}A Dependency to Resolve the Version
Branching inside every endpoint gets messy. Extract the version logic into a reusable dependency that validates the header once and rejects unsupported versions with a clean 406.
Any endpoint can now depend on api_version and trust it is one of the supported values.
from fastapi import FastAPI, Header, HTTPException, Depends
app = FastAPI()
SUPPORTED = {1, 2}
def api_version(x_api_version: int = Header(default=1)) -> int:
if x_api_version not in SUPPORTED:
raise HTTPException(
status_code=406,
detail=f"Unsupported API version {x_api_version}",
)
return x_api_version
@app.get("/users/{user_id}")
def get_user(user_id: int, version: int = Depends(api_version)):
return {"id": user_id, "version": version}Strategy 3: Media-Type (Content Negotiation) Versioning
The most RESTful but least common approach. The client asks for a specific representation via the Accept header using a vendor media type:
Accept: application/vnd.myapp.v1+jsonAccept: application/vnd.myapp.v2+json
The URL identifies the resource; the media type identifies the representation/version. This is true HTTP content negotiation.
- Pros: URLs are clean and semantically pure; you version the representation, not the resource.
- Cons: verbose, hard for casual clients, weak tooling support, easy to typo.
Parsing the Vendor Media Type
The core of media-type versioning is parsing the vendor string into a version number. That parsing is pure Python and easy to unit-test on its own, independent of any framework.
Below we extract the vN token from an Accept value, defaulting to v1 when it is missing or malformed.
import re
PATTERN = re.compile(r"application/vnd\.myapp\.v(\d+)\+json")
def parse_version(accept: str, default: int = 1) -> int:
match = PATTERN.search(accept or "")
return int(match.group(1)) if match else default
# A few quick checks
print(parse_version("application/vnd.myapp.v2+json")) # 2
print(parse_version("application/json")) # 1 (default)
print(parse_version("application/vnd.myapp.v10+json")) # 10Wiring Media-Type Versioning into FastAPI
With the parser ready, plug it into a dependency that reads the Accept header. Endpoints stay clean and resource-focused while the version comes from content negotiation.
import re
from fastapi import FastAPI, Header, Depends
app = FastAPI()
PATTERN = re.compile(r"application/vnd\.myapp\.v(\d+)\+json")
def accept_version(accept: str = Header(default="")) -> int:
m = PATTERN.search(accept)
return int(m.group(1)) if m else 1
@app.get("/users/{user_id}")
def get_user(user_id: int, version: int = Depends(accept_version)):
if version >= 2:
return {"id": user_id, "first_name": "Ada", "last_name": "Lovelace"}
return {"id": user_id, "name": "Ada Lovelace"}Keeping Versions DRY with Transformers
Duplicating business logic per version rots fast. A cleaner pattern: compute the data once in a canonical internal shape, then run a small per-version transformer that adapts it to the contract each version promised.
This isolates contract differences in tiny, testable functions instead of forking your whole handler.
def canonical_user(user_id: int) -> dict:
return {"id": user_id, "first": "Ada", "last": "Lovelace"}
def to_v1(u: dict) -> dict:
return {"id": u["id"], "name": f"{u['first']} {u['last']}"}
def to_v2(u: dict) -> dict:
return {"id": u["id"], "first_name": u["first"], "last_name": u["last"]}
VERSIONS = {1: to_v1, 2: to_v2}
def render(user_id: int, version: int) -> dict:
return VERSIONS[version](canonical_user(user_id))
print(render(42, 1)) # {'id': 42, 'name': 'Ada Lovelace'}
print(render(42, 2)) # {'id': 42, 'first_name': 'Ada', 'last_name': 'Lovelace'}Caching, Vary, and Documentation Pitfalls
Header and media-type versioning have a sharp edge: caches. If a proxy caches /users/42 without knowing about your version header, a v1 client may receive a cached v2 body.
- Always send
Vary: X-API-Version(orVary: Acceptfor media-type versioning) so caches key on it. - URL versioning sidesteps this entirely because each version has a distinct URL.
- Header/media-type versions are also harder to see in the auto-generated
/docsschema, since the path looks identical across versions.
Choosing a Strategy and Deprecating Cleanly
Practical guidance for the bootcamp:
- URL versioning is the default for public REST APIs: visible, cache-friendly, easy to onboard.
- Header versioning suits internal services that want stable URLs and control both client and server.
- Media-type versioning fits hypermedia/REST purists; rare in practice.
Whatever you pick, ship a deprecation plan: announce a sunset date, return a Deprecation/Sunset header on old versions, and keep them alive long enough for clients to migrate.
from fastapi import APIRouter
from fastapi.responses import JSONResponse
v1 = APIRouter(prefix="/v1")
@v1.get("/users/{user_id}")
def get_user_v1(user_id: int):
body = {"id": user_id, "name": "Ada Lovelace"}
headers = {
"Deprecation": "true",
"Sunset": "Wed, 31 Dec 2025 23:59:59 GMT",
"Link": '</v2/users>; rel="successor-version"',
}
return JSONResponse(content=body, headers=headers)Quick Check: Picking the Right Versioning Approach
Apply what you learned about the trade-offs between the three strategies.
Recap: Versioning Without Breakage
You compared three ways to version a FastAPI API:
- URL path (
/v1/users): visible, cache-friendly, easy to route via per-versionAPIRouterprefixes. The default for public APIs. - Header (
X-API-Version): clean URLs, resolved with a dependency; rememberVaryfor caches. - Media-type (
Accept: application/vnd.myapp.vN+json): purest REST, parsed from the Accept header; rare and verbose.
Keep routes grouped by resource and mounted under version routers, isolate contract differences in small per-version transformers, and always pair a new version with a clear deprecation/sunset plan so clients upgrade smoothly.
AI 튜터와 함께 FastAPI Backend Development Bootcamp을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 21
- 레슨
- 84
자주 묻는 질문
“URL, 헤더 및 미디어 유형 버전 관리” 강의는 무료인가요?
네 — “URL, 헤더 및 미디어 유형 버전 관리” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 FastAPI Backend Development Bootcamp 강의 전체를 잠금 해제할 수 있습니다. FastAPI Backend Development Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
“URL, 헤더 및 미디어 유형 버전 관리”에서 뭘 배우나요?
버전 관리 방식을 비교하고 깔끔한 경로 그룹을 구현해 클라이언트가 중단 없이 업그레이드하도록 합니다. 브라우저에서 직접 실행하는 실습 코드로 FastAPI Backend Development Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
FastAPI Backend Development Bootcamp을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 FastAPI Backend Development Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“URL, 헤더 및 미디어 유형 버전 관리” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 FastAPI Backend Development Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 FastAPI Backend Development Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- URL, 헤더 및 미디어 유형 버전 관리
- 대규모 환경에서의 커서와 오프셋 페이지 매김
- 동적 필터링 및 정렬 매개변수
- 안정적인 응답 봉투 설계