Skip to main content

FastAPI Tag Organization

Introduction

As your FastAPI application grows in size and complexity, organizing your endpoints becomes increasingly important. Tags in FastAPI provide a powerful way to logically group your API endpoints, making your documentation more organized and your API easier to navigate. This organization is especially valuable for other developers who will interact with your API through the automatically generated Swagger UI or ReDoc interfaces.

In this guide, we'll explore how to effectively use tags in FastAPI to organize your API endpoints, improve your documentation, and make your API more maintainable.

Understanding Tags in FastAPI

Tags in FastAPI are simple string identifiers that you can attach to your API route operations (endpoints). They serve two main purposes:

  1. Documentation Organization: Tags group related endpoints in the automatically generated API documentation.
  2. Code Organization: Tags help you logically group related functionality in your codebase.

Basic Tag Usage

Let's start with a simple example of how to apply tags to your FastAPI endpoints:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "John"}, {"name": "Jane"}]

@app.post("/users/", tags=["users"])
async def create_user(name: str):
return {"message": f"User {name} created"}

@app.get("/items/", tags=["items"])
async def get_items():
return [{"name": "Laptop"}, {"name": "Smartphone"}]

@app.post("/items/", tags=["items"])
async def create_item(name: str):
return {"message": f"Item {name} created"}

In this example, we've organized our endpoints into two tags: "users" and "items". When you access the Swagger UI (at /docs), these endpoints will be grouped under their respective tag sections.

Multiple Tags for an Endpoint

You can apply multiple tags to a single endpoint if it relates to multiple categories:

python
@app.get("/user-items/{user_id}", tags=["users", "items"])
async def get_user_items(user_id: int):
return {"user_id": user_id, "items": [{"name": "Laptop"}, {"name": "Smartphone"}]}

This endpoint will appear in both the "users" and "items" sections of your API documentation.

Tag Ordering and Customization

FastAPI allows you to customize how tags are displayed in your documentation by defining them in your FastAPI application:

python
from fastapi import FastAPI

app = FastAPI(
title="My Super API",
description="This is a very fancy API",
version="1.0.0",
openapi_tags=[
{
"name": "users",
"description": "Operations related to users",
},
{
"name": "items",
"description": "Operations related to items",
"externalDocs": {
"description": "Items external docs",
"url": "https://example.com/items/",
},
},
]
)

# Your endpoints here...

The openapi_tags parameter lets you:

  • Define the order in which tags appear in your documentation
  • Add descriptions to your tags
  • Link to external documentation

Organizing Routes with Tags and APIRouter

For larger applications, you can combine tags with FastAPI's APIRouter to organize your code more effectively:

python
from fastapi import FastAPI, APIRouter

app = FastAPI()

# Create routers with predefined tags
user_router = APIRouter(prefix="/users", tags=["users"])
item_router = APIRouter(prefix="/items", tags=["items"])

# Add routes to the user router
@user_router.get("/")
async def get_users():
return [{"name": "John"}, {"name": "Jane"}]

@user_router.post("/")
async def create_user(name: str):
return {"message": f"User {name} created"}

# Add routes to the item router
@item_router.get("/")
async def get_items():
return [{"name": "Laptop"}, {"name": "Smartphone"}]

@item_router.post("/")
async def create_item(name: str):
return {"message": f"Item {name} created"}

# Include the routers in the main app
app.include_router(user_router)
app.include_router(item_router)

This approach has several advantages:

  • You can organize related routes in separate files
  • Every route in a router automatically gets the same prefix and tags
  • Code is more modular and maintainable

Real-World Example: E-commerce API

Let's look at a more comprehensive example for an e-commerce API with multiple tags:

python
from fastapi import FastAPI, APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI(
title="E-commerce API",
description="API for managing an e-commerce platform",
version="1.0.0",
openapi_tags=[
{"name": "users", "description": "Operations related to users and authentication"},
{"name": "products", "description": "Product catalog management"},
{"name": "orders", "description": "Order processing and history"},
{"name": "cart", "description": "Shopping cart operations"},
{"name": "admin", "description": "Administrative operations"}
]
)

# Models
class User(BaseModel):
id: Optional[int] = None
username: str
email: str

class Product(BaseModel):
id: Optional[int] = None
name: str
price: float
description: str

# User routes
@app.post("/users/", tags=["users"])
async def create_user(user: User):
return {"id": 1, **user.dict()}

@app.get("/users/{user_id}", tags=["users"])
async def get_user(user_id: int):
return {"id": user_id, "username": "johndoe", "email": "[email protected]"}

# Product routes
@app.get("/products/", tags=["products"])
async def list_products():
return [
{"id": 1, "name": "Laptop", "price": 999.99},
{"id": 2, "name": "Smartphone", "price": 499.99}
]

@app.get("/products/{product_id}", tags=["products"])
async def get_product(product_id: int):
return {"id": product_id, "name": "Laptop", "price": 999.99}

@app.post("/products/", tags=["products", "admin"])
async def create_product(product: Product):
return {"id": 3, **product.dict()}

# Order routes
@app.get("/orders/", tags=["orders"])
async def list_orders():
return [
{"id": 1, "user_id": 1, "products": [1, 2], "total": 1499.98},
{"id": 2, "user_id": 2, "products": [1], "total": 999.99}
]

@app.post("/orders/", tags=["orders", "cart"])
async def create_order():
return {"message": "Order created", "order_id": 3}

# Cart routes
@app.get("/cart/", tags=["cart"])
async def get_cart():
return {"items": [{"product_id": 1, "quantity": 2}]}

@app.post("/cart/add", tags=["cart"])
async def add_to_cart(product_id: int, quantity: int = 1):
return {"message": f"Added {quantity} of product {product_id} to cart"}

# Admin routes
@app.get("/admin/stats", tags=["admin"])
async def admin_stats():
return {"users": 100, "products": 50, "orders": 200, "revenue": 15000}

When you view this API in Swagger UI, you'll see the endpoints neatly organized under their respective tags, making it much easier to navigate and understand the API's capabilities.

Tag Best Practices

Here are some best practices when using tags in your FastAPI applications:

  1. Use Consistent Naming: Decide on a naming convention for your tags and stick to it.

  2. Group by Resource or Function: Tags typically represent either resources (users, products) or functional areas (authentication, admin).

  3. Don't Over-Tag: Too many tags can be as confusing as no tags. Aim for a reasonable number.

  4. Use APIRouter for Large Applications: For larger apps, combine tags with APIRouter to organize your code into modules.

  5. Document Your Tags: Always provide descriptions for your tags in the openapi_tags parameter.

  6. Consider Tag Hierarchy: If you have many endpoints, consider creating a tag hierarchy (e.g., "products", "products-inventory", "products-pricing").

  7. Separate Public and Admin APIs: Use different tags for public-facing endpoints versus administrative endpoints.

Summary

FastAPI tags provide a simple but powerful way to organize your API endpoints, both in the code and in the automatically generated documentation. By using tags strategically, you can:

  • Create a more intuitive API structure
  • Improve the developer experience for API consumers
  • Make your code more maintainable
  • Separate concerns by functionality or resource type

Whether you're building a small API or a large enterprise system, proper tag organization can significantly improve the clarity and maintainability of your FastAPI application.

Additional Resources

Exercises

  1. Refactor an existing FastAPI application to use appropriate tags.
  2. Create a new FastAPI application for a blog with at least five different tags.
  3. Implement tag descriptions and external documentation links for an API.
  4. Combine APIRouter and tags to create a modular API with different files for each resource type.
  5. Create a hierarchical tag structure for a complex application domain.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)