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 flask import Blueprint, jsonify, request, abort v1 = Blueprint("users_v1", __name__) v2 = Blueprint("users_v2", __name__)
Versioning a Flask API with Blueprints
api versioning
blueprints
rest
Intermediate
7 steps
python
import time import threading from enum import Enum from functools import wraps
Building a circuit breaker in Python
circuit-breaker
resilience
decorators
Advanced
7 steps
python
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector from django.core.cache import cache from django.db.models import F from django.http import JsonResponse
Ranked full-text search in Django
full-text-search
debouncing
caching
Advanced
9 steps
python
import os from datetime import timedelta
Class-based config in a Flask app factory
configuration
app-factory
inheritance
Intermediate
7 steps
python
import time from flask import Flask, g, request
Timing requests with Flask hooks
middleware
request-lifecycle
instrumentation
Intermediate
5 steps
python
from pathlib import Path from PIL import Image, ImageOps
Batch image resizing with Pillow
image-processing
batch-jobs
error-handling
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.