python
48 lines · 6 steps
Versioning a FastAPI API with mounted sub-apps
Two response schemas and two routers are wired into separate sub-applications so one endpoint can evolve without breaking older clients.
Explained by
highlit
1from fastapi import APIRouter, FastAPI, HTTPException
2from pydantic import BaseModel
3
4
5class UserV1(BaseModel):
6 id: int
7 name: str
8
9
10class UserV2(BaseModel):
11 id: int
12 first_name: str
13 last_name: str
14 email: str
15
16
17v1 = APIRouter()
18v2 = APIRouter()
19
20_users = {1: {"first_name": "Ada", "last_name": "Lovelace", "email": "ada@example.com"}}
21
22
23@v1.get("/users/{user_id}", response_model=UserV1)
24async def get_user_v1(user_id: int):
25 user = _users.get(user_id)
26 if user is None:
27 raise HTTPException(status_code=404, detail="user not found")
28 return UserV1(id=user_id, name=f"{user['first_name']} {user['last_name']}")
29
30
31@v2.get("/users/{user_id}", response_model=UserV2)
32async def get_user_v2(user_id: int):
33 user = _users.get(user_id)
34 if user is None:
35 raise HTTPException(status_code=404, detail="user not found")
36 return UserV2(id=user_id, **user)
37
38
39app = FastAPI(title="Accounts API")
40
41v1_app = FastAPI(title="Accounts API v1")
42v1_app.include_router(v1)
43
44v2_app = FastAPI(title="Accounts API v2")
45v2_app.include_router(v2)
46
47app.mount("/api/v1", v1_app)
48app.mount("/api/v2", v2_app)
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Distinct Pydantic response models let each API version expose exactly the shape its clients expect.
- 2Mounting separate FastAPI apps isolates versions with their own docs, routers, and titles under one host.
- 3A single backing data store can serve multiple schemas by mapping fields per version at the boundary.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
python
from typing import Any, Sequence, Mapping def render_markdown_table(
Rendering an aligned Markdown table in Python
string formatting
data transformation
closures
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/versioning-a-fastapi-api-with-mounted-sub-apps-explained-python-32ce/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.