Lesson 6 · FastAPI deep dive
FastAPI Tutorial #6: Path Operation Configuration & Better Docs
Video six of the eighteen-part series: shaping how endpoints appear in the auto-generated docs. Tags, summaries, descriptions, deprecation, and documenting…
- CourseFastAPI deep dive
- Lesson6 of 12
- Video17 min
- FormatJupyter notebook · 10 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbFastAPI Deep-Dive, Video 6: Path Operation Configuration#
- Video six of the eighteen-part series: shaping how endpoints appear in the auto-generated docs.
- Tags, summaries, descriptions, deprecation, and documenting multiple response codes.
- Let's get into it.
Part 1: tags - Grouping Endpoints in the Docs#
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get('/users', tags=['users'])
def list_users():
return [{'id': 1, 'name': 'Alex'}]
@app.get('/orders', tags=['orders'])
def list_orders():
return [{'id': 100, 'total': 42.5}]
client = TestClient(app)
schema = app.openapi()
print(schema['paths']['/users']['get']['tags'])
print(schema['paths']['/orders']['get']['tags'])
Part 2: summary and description#
@app.get(
'/products',
summary='List all products',
description='Returns every product currently in the catalog, unfiltered.',
)
def list_products():
return [{'id': 1, 'name': 'Mug'}]
app.openapi_schema = None
schema = app.openapi()
print(schema['paths']['/products']['get']['summary'])
print(schema['paths']['/products']['get']['description'])
Part 3: Docstrings Become the Description Automatically#
@app.get('/categories')
def list_categories():
"""Return the full list of product categories, sorted alphabetically."""
return ['electronics', 'kitchen', 'outdoor']
app.openapi_schema = None
schema = app.openapi()
print(schema['paths']['/categories']['get']['description'])
Part 4: response_description#
@app.post(
'/products',
status_code=201,
response_description='The newly created product',
)
def create_product():
return {'id': 2, 'name': 'Plate'}
app.openapi_schema = None
schema = app.openapi()
print(schema['paths']['/products']['post']['responses']['201']['description'])
Part 5: deprecated=True#
@app.get('/legacy-users', deprecated=True)
def legacy_list_users():
return [{'id': 1, 'name': 'Alex'}]
app.openapi_schema = None
schema = app.openapi()
print(schema['paths']['/legacy-users']['get']['deprecated'])
print(client.get('/legacy-users').status_code)
Part 6: responses - Documenting Multiple Status Codes#
from fastapi import HTTPException
@app.get(
'/products/{product_id}',
responses={404: {'description': 'Product not found'}},
)
def get_product(product_id: int):
if product_id != 1:
raise HTTPException(status_code=404, detail='not found')
return {'id': 1, 'name': 'Mug'}
app.openapi_schema = None
schema = app.openapi()
print(schema['paths']['/products/{product_id}']['get']['responses']['404'])
print(client.get('/products/1').status_code)
print(client.get('/products/99').status_code)
Part 7: include_in_schema=False - Hiding Internal Endpoints#
@app.get('/internal/debug', include_in_schema=False)
def debug_info():
return {'status': 'ok', 'internal': True}
app.openapi_schema = None
schema = app.openapi()
print('/internal/debug' in schema['paths'])
print(client.get('/internal/debug').json())
Part 8: operation_id - Explicit Client Generation Names#
@app.get('/reports/summary', operation_id='get_summary_report')
def summary_report():
return {'total_orders': 12, 'total_revenue': 458.75}
app.openapi_schema = None
schema = app.openapi()
print(schema['paths']['/reports/summary']['get']['operationId'])
Part 9: Combining Several Configuration Options Together#
@app.delete(
'/products/{product_id}',
tags=['products'],
summary='Delete a product',
description='Permanently removes a product from the catalog by id.',
response_description='Confirmation that the product was deleted',
responses={404: {'description': 'Product not found'}},
)
def delete_product(product_id: int):
if product_id != 1:
raise HTTPException(status_code=404, detail='not found')
return {'deleted': product_id}
app.openapi_schema = None
schema = app.openapi()
entry = schema['paths']['/products/{product_id}']['delete']
print(entry['tags'], entry['summary'])
print(client.delete('/products/1').json())
Part 10: A Real Pattern - a Fully Documented Endpoint Group#
inventory_app = FastAPI()
@inventory_app.get(
'/inventory/{sku}',
tags=['inventory'],
summary='Get stock level for a SKU',
description='Looks up the current stock quantity for a given product SKU.',
responses={404: {'description': 'SKU not found'}},
)
def get_stock(sku: str):
catalog = {'MUG-1': 40, 'PLATE-2': 15}
if sku not in catalog:
raise HTTPException(status_code=404, detail='unknown sku')
return {'sku': sku, 'quantity': catalog[sku]}
@inventory_app.post(
'/inventory/{sku}/restock',
tags=['inventory'],
summary='Restock a SKU',
description='Adds units to the current stock quantity for a given product SKU.',
responses={404: {'description': 'SKU not found'}},
)
def restock(sku: str, amount: int):
catalog = {'MUG-1': 40, 'PLATE-2': 15}
if sku not in catalog:
raise HTTPException(status_code=404, detail='unknown sku')
return {'sku': sku, 'quantity': catalog[sku] + amount}
inventory_client = TestClient(inventory_app)
print(inventory_client.get('/inventory/MUG-1').json())
print(inventory_client.post('/inventory/MUG-1/restock', params={'amount': 10}).json())
print(inventory_client.get('/inventory/UNKNOWN').status_code)
Wrap-Up: What You Learned#
- tags group related endpoints together under the same heading in the docs.
- summary and description supply a short title and longer explanation for an endpoint.
- A handler's own docstring becomes the description automatically when none is given.
- response_description labels what the success response itself represents.
- deprecated=True marks an endpoint as outdated in the docs without breaking it.
- responses documents additional status codes, like 404, with their own description.
- include_in_schema=False hides an endpoint from the docs while it keeps working.
- operation_id gives an endpoint a stable, explicit name for client SDK generation.
- All of these options stack together freely on a single endpoint.
- That wraps up path operation configuration. Next up: Dependency Injection Basics.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



