Skip to content

Commit 1effb87

Browse files
stackjayjaygaha
authored andcommitted
day #48 fastapi #13 advanced docs & openapi customization
1 parent 33af210 commit 1effb87

15 files changed

Lines changed: 1441 additions & 0 deletions

File tree

workspace/7_framework/fastapi/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ uvicorn main:app --reload
6565

6666
## Tutorials
6767

68+
We will cover the important concepts of FastAPI in this series of tutorials. WE will cover these topics in 14 days series of tutorials.
69+
6870
- [Day 01: FastAPI Basics & Testing](day01/README.md)
6971
Learn how to build your first FastAPI application, create simple endpoints, and write automated tests.
7072
_Includes: project structure, endpoint examples, running instructions, and test coverage._
@@ -113,6 +115,10 @@ uvicorn main:app --reload
113115
Learn how to build a real-time chat application with WebSockets, manage connections, and broadcast messages.
114116
_Includes: `WebSocket`, connection management, client-side integration, and `pytest` for WebSockets._
115117

118+
- [Day 13: Advanced Documentation & OpenAPI Customization](day13/README.md)
119+
Learn to create rich, detailed, and customized API documentation with OpenAPI, including custom UIs, advanced schemas, and versioning.
120+
_Includes: OpenAPI customization, custom documentation UIs, `pydantic-settings`, rich metadata, and documentation testing._
121+
116122
---
117123

118124
## Recommended Project Structure
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# FastAPI Day 13: Advanced OpenAPI and Documentation
2+
3+
Welcome to **Day 13** of the FastAPI tutorial series! While FastAPI provides excellent out-of-the-box documentation, this tutorial focuses on elevating it to a professional standard. You'll learn how to customize the OpenAPI schema, add detailed metadata, and create a rich, user-friendly experience for your API consumers.
4+
5+
---
6+
7+
## What You'll Learn
8+
9+
- **Custom OpenAPI Schema**: Override FastAPI's default OpenAPI generation to add custom server information, security schemes, and global parameters.
10+
- **Centralized Configuration**: Use `pydantic-settings` to manage all API metadata (like title, version, and contact info) in a single, clean configuration file.
11+
- **Rich Endpoint Documentation**: Enhance your endpoints with summaries, detailed descriptions, response examples, and clear error models.
12+
- **Custom Documentation UIs**: Serve customized versions of Swagger UI and ReDoc with enhanced display options.
13+
- **API Versioning & Deprecation**: Document API versions and clearly mark endpoints as deprecated to guide users through API evolution.
14+
- **Testing Documentation**: Write automated tests to ensure your documentation remains accurate and complete.
15+
16+
---
17+
18+
## Key Concepts
19+
20+
This project is a mock e-commerce API with advanced documentation features.
21+
22+
- `src/main.py`: Contains the main FastAPI application, custom OpenAPI generation logic, and routes for custom documentation UIs.
23+
- `src/config.py`: Uses `pydantic-settings` to manage all documentation-related metadata.
24+
- `src/routers/`: API routers for users, products, and orders, with extensive documentation.
25+
- `src/models/schemas.py`: Pydantic models with detailed field descriptions and examples.
26+
- `tests/test_documentation.py`: `pytest` tests for validating the OpenAPI schema and custom documentation pages.
27+
28+
### 1. Centralized Metadata with `pydantic-settings`
29+
30+
To keep our main application file clean and manage metadata efficiently, we define everything in a `Settings` class. This includes the app's title, version, contact information, license, and tag metadata for grouping endpoints.
31+
32+
```python-beginner/workspace/7_framework/fastapi/day13/src/config.py#L5-L53
33+
class Settings(BaseSettings):
34+
app_name: str = "FastAPI E-commerce API"
35+
app_version: str = "2.1.0"
36+
app_description: str = """
37+
A comprehensive e-commerce API built with FastAPI.
38+
39+
## Features
40+
41+
* **Users**: Create, read, update, and delete user accounts
42+
* **Products**: Manage product catalog with categories and inventory
43+
* **Orders**: Handle order processing and tracking
44+
45+
## Authentication
46+
47+
Some endpoints require authentication. Use the `/auth/login` endpoint to obtain a token.
48+
"""
49+
50+
contact_info: Dict[str, Any] = {
51+
"name": "API Support Team",
52+
"email": "support@ecommerce-api.com",
53+
"url": "https://ecommerce-api.com/support"
54+
}
55+
56+
license_info: Dict[str, Any] = {
57+
"name": "MIT License",
58+
"url": "https://opensource.org/licenses/MIT"
59+
}
60+
61+
tags_metadata: list = [
62+
{
63+
"name": "users",
64+
"description": "User management operations. Create, read, update, and delete user accounts.",
65+
"externalDocs": {
66+
"description": "User Guide",
67+
"url": "https://docs.ecommerce-api.com/users"
68+
}
69+
},
70+
{
71+
"name": "products",
72+
"description": "Product catalog management. Handle inventory, categories, and pricing.",
73+
"externalDocs": {
74+
"description": "Product Management Guide",
75+
"url": "https://docs.ecommerce-api.com/products"
76+
}
77+
},
78+
{
79+
"name": "orders",
80+
"description": "Order processing and tracking. Handle customer purchases and fulfillment.",
81+
},
82+
{
83+
"name": "admin",
84+
"description": "Administrative operations. Requires admin privileges.",
85+
}
86+
]
87+
```
88+
89+
### 2. Customizing the OpenAPI Schema
90+
91+
We create a `custom_openapi` function to programmatically modify the schema. This allows us to add environment-specific server URLs (dev, staging, prod), define security schemes like JWT and API Keys, and inject global headers.
92+
93+
```python-beginner/workspace/7_framework/fastapi/day13/src/main.py#L14-L83
94+
def custom_openapi():
95+
"""Generate custom OpenAPI schema with enhanced metadata"""
96+
if app.openapi_schema:
97+
return app.openapi_schema
98+
99+
openapi_schema = get_openapi(
100+
title=settings.app_name,
101+
version=settings.app_version,
102+
description=settings.app_description,
103+
routes=app.routes,
104+
contact=settings.contact_info,
105+
license_info=settings.license_info,
106+
)
107+
108+
# Add custom server information
109+
openapi_schema["servers"] = [
110+
{
111+
"url": "https://api.ecommerce.com",
112+
"description": "Production server"
113+
},
114+
{
115+
"url": "https://staging-api.ecommerce.com",
116+
"description": "Staging server"
117+
},
118+
{
119+
"url": "http://localhost:8000",
120+
"description": "Development server"
121+
}
122+
]
123+
124+
# Add security schemes
125+
openapi_schema["components"]["securitySchemes"] = {
126+
"bearerAuth": {
127+
"type": "http",
128+
"scheme": "bearer",
129+
"bearerFormat": "JWT",
130+
"description": "Enter your JWT token in the format: Bearer <token>"
131+
},
132+
"apiKey": {
133+
"type": "apiKey",
134+
"in": "header",
135+
"name": "X-API-Key",
136+
"description": "API key for authentication"
137+
}
138+
}
139+
140+
# Add global security requirement
141+
openapi_schema["security"] = [
142+
{"bearerAuth": []},
143+
{"apiKey": []}
144+
]
145+
146+
# Enhance path descriptions
147+
for path, path_item in openapi_schema["paths"].items():
148+
for method, operation in path_item.items():
149+
if method in ["get", "post", "put", "delete", "patch"]:
150+
# Add custom headers to all operations
151+
if "parameters" not in operation:
152+
operation["parameters"] = []
153+
154+
operation["parameters"].extend([
155+
{
156+
"name": "X-Request-ID",
157+
"in": "header",
158+
"required": False,
159+
"schema": {
160+
"type": "string",
161+
"format": "uuid",
162+
"description": "Unique request identifier for tracking"
163+
}
164+
}
165+
])
166+
167+
app.openapi_schema = openapi_schema
168+
return app.openapi_schema
169+
```
170+
171+
### 3. Detailed Endpoint Documentation
172+
173+
In each router, we use decorator parameters like `summary`, `description`, and a `responses` dictionary to provide rich context. This includes examples for successful responses and structured models for errors.
174+
175+
```python-beginner/workspace/7_framework/fastapi/day13/src/routers/users.py#L90-L132
176+
@router.get(
177+
"/{user_id}",
178+
response_model=UserResponse,
179+
summary="Get user by ID",
180+
description="Retrieve a specific user by their unique identifier.",
181+
responses={
182+
200: {
183+
"description": "User found successfully",
184+
"content": {
185+
"application/json": {
186+
"example": {
187+
"id": 1,
188+
"email": "john.doe@example.com",
189+
"first_name": "John",
190+
"last_name": "Doe",
191+
"role": "customer",
192+
"is_active": True,
193+
"created_at": "2023-01-01T00:00:00"
194+
}
195+
}
196+
}
197+
},
198+
404: {
199+
"description": "User not found",
200+
"model": ErrorResponse,
201+
"content": {
202+
"application/json": {
203+
"example": {
204+
"detail": "User not found",
205+
"error_code": "USER_NOT_FOUND"
206+
}
207+
}
208+
}
209+
}
210+
}
211+
)
212+
async def get_user(
213+
user_id: int = Path(..., gt=0, description="The unique identifier of the user", examples=[1])
214+
):
215+
"""
216+
Get a specific user by ID.
217+
218+
Returns detailed information about a user including their profile data and account status.
219+
"""
220+
user = next((user for user in fake_users_db if user["id"] == user_id), None)
221+
if not user:
222+
raise HTTPException(
223+
status_code=status.HTTP_404_NOT_FOUND,
224+
detail="User not found"
225+
)
226+
return user
227+
```
228+
229+
### 4. Testing Your Documentation
230+
231+
It's crucial to test your documentation just like your code. Using `TestClient`, we can fetch `/api/v1/openapi.json` and assert that our customizations—like the title, version, and server information—are present and correct.
232+
233+
```python-beginner/workspace/7_framework/fastapi/day13/tests/test_documentation.py#L15-L26
234+
def test_openapi_schema_generation(self):
235+
"""Test that OpenAPI schema is generated correctly"""
236+
response = client.get("/api/v1/openapi.json")
237+
assert response.status_code == 200
238+
239+
schema = response.json()
240+
assert schema["info"]["title"] == "FastAPI E-commerce API"
241+
assert schema["info"]["version"] == "2.1.0"
242+
assert "description" in schema["info"]
243+
assert schema["info"]["contact"]["name"] == "API Support Team"
244+
assert schema["info"]["license"]["name"] == "MIT License"
245+
```
246+
247+
---
248+
249+
## Next Steps
250+
251+
- Navigate to the `day13` directory: `cd day13`.
252+
- Install the dependencies: `pip install -r requirements.txt`.
253+
- Run the application: `uvicorn src.main:app --reload`.
254+
- Explore the enhanced documentation at `http://localhost:8000/docs` (Swagger) and `http://localhost:8000/redoc` (ReDoc).
255+
- Run the automated tests with `python -m pytest`.
256+
257+
---
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
# asyncio_default_fixture_loop_scope = function
3+
pythonpath = . src
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fastapi>=0.115.0
2+
uvicorn[standard]>=0.30.0
3+
pydantic>=2.7.0
4+
pydantic-settings>=2.10.1
5+
dnspython>=2.7.0
6+
email-validator>=2.2.0
7+
pytest==7.4.3
8+
httpx==0.25.1
9+
pytest-asyncio>=0.21.1

workspace/7_framework/fastapi/day13/src/__init__.py

Whitespace-only changes.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from pydantic_settings import BaseSettings
2+
from typing import Dict, Any
3+
4+
5+
class Settings(BaseSettings):
6+
app_name: str = "FastAPI E-commerce API"
7+
app_version: str = "2.1.0"
8+
app_description: str = """
9+
A comprehensive e-commerce API built with FastAPI.
10+
11+
## Features
12+
13+
* **Users**: Create, read, update, and delete user accounts
14+
* **Products**: Manage product catalog with categories and inventory
15+
* **Orders**: Handle order processing and tracking
16+
17+
## Authentication
18+
19+
Some endpoints require authentication. Use the `/auth/login` endpoint to obtain a token.
20+
"""
21+
22+
contact_info: Dict[str, Any] = {
23+
"name": "API Support Team",
24+
"email": "support@ecommerce-api.com",
25+
"url": "https://ecommerce-api.com/support"
26+
}
27+
28+
license_info: Dict[str, Any] = {
29+
"name": "MIT License",
30+
"url": "https://opensource.org/licenses/MIT"
31+
}
32+
33+
tags_metadata: list = [
34+
{
35+
"name": "users",
36+
"description": "User management operations. Create, read, update, and delete user accounts.",
37+
"externalDocs": {
38+
"description": "User Guide",
39+
"url": "https://docs.ecommerce-api.com/users"
40+
}
41+
},
42+
{
43+
"name": "products",
44+
"description": "Product catalog management. Handle inventory, categories, and pricing.",
45+
"externalDocs": {
46+
"description": "Product Management Guide",
47+
"url": "https://docs.ecommerce-api.com/products"
48+
}
49+
},
50+
{
51+
"name": "orders",
52+
"description": "Order processing and tracking. Handle customer purchases and fulfillment.",
53+
},
54+
{
55+
"name": "admin",
56+
"description": "Administrative operations. Requires admin privileges.",
57+
}
58+
]
59+
60+
61+
settings = Settings()

0 commit comments

Comments
 (0)