Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 46

Router Prefixing for API Versioning

Recommended resources

Hello! Welcome back to our course on mastering FastAPI.

In the last lesson, we established a clean and robust architecture using the service-repository pattern. We successfully refactored a full set of CRUD endpoints, resulting in "thin" routers that delegate all business and data logic to dedicated service and repository layers. This separation of concerns is fundamental to building scalable applications.

This lesson directly addresses the learning outcome: Implement API versioning by prefixing routers.

Now that we have this modular structure, we'll tackle a critical aspect of long-term API maintenance: evolution. APIs are not static; they change to accommodate new features and requirements. We will learn how to manage these changes without breaking existing client applications by implementing API versioning. You'll see how the APIRouter system we've been using makes this a surprisingly elegant process.

Why Do We Need API Versioning?

Imagine your API is live and being used by a mobile app. You decide to add a new, required description field to your Item model. If you deploy this change, the existing mobile app, which doesn't know about the description field, will start receiving errors when it tries to create new items. The app is now broken for all your users.

API versioning is the solution. It allows you to introduce breaking changes in a new version (e.g., v2) while continuing to support the old version (v1). This gives your clients (the mobile app developers, in this case) time to update their code to support the new version.

To understand the common strategies for versioning, let's start with a brief reading.

FastAPI Versioning. Continue building your API without… | Medium

This article, 'FastAPI Versioning', begins by explaining the two most common approaches to API versioning. It will give you the essential background theory before we dive into the implementation.

Please read the first section of the article, from the start down to (but not including) 'Implementing FastAPI Versioning'. Focus on understanding the difference between Header Versioning and URL Versioning, and their respective pros and cons.

As the article notes, while Header Versioning can be seen as more "purely" RESTful, URL Versioning (e.g., /api/v1/items) is extremely common due to its simplicity and clarity. It's easy to see which version you're calling, easy to test in a browser, and easy to document. For these reasons, we will focus on implementing URL versioning.


Implementing URL Versioning with APIRouter

You've already done most of the hard work for this in previous lessons! The key lies in the APIRouter and the prefix argument of app.include_router().

Remember in our lesson on organizing routes, we used APIRouter to split our user and post endpoints into separate files. We then included them in main.py like this (as demonstrated in the Corey Schafer video we watched):




# main.py
app.include_router(users_router, prefix="/api/users", tags=["Users"])
app.include_router(posts_router, prefix="/api/posts", tags=["Posts"])

The strategy for versioning is a natural extension of this. We simply add a version identifier to the prefix.

The most scalable approach involves creating a project structure that reflects the versions. Let's see how this is done in practice.

API Versioning with FastAPI Made EASY (Do This Before It’s Too Late)

This video, 'API Versioning with FastAPI Made EASY', provides an excellent, practical walkthrough of three ways to implement versioning. We will focus on the more structured and scalable approaches that align with our goal of building professional applications.

Please watch from 02:11 to 10:20. The video first demonstrates a quick (but less structured) method. Then, pay close attention to the two more scalable solutions presented ('Project 2' and 'Project 3'), which organize versions into separate folders. Notice how APIRouter and prefixes are used to assemble the final versioned API.


A Scalable Structure for Versioning

As the video demonstrated, the most robust approach is to organize your project files by version. Let's solidify this with a textual example and a clear code structure.

The core idea is to create a main router for each version, which in turn includes the routers for each resource. This creates a "stack" of prefixes.

Let's look at a well-structured example from the tutorial by Christopher GS.

The Ultimate FastAPI Tutorial Part 8 - Project Structure, Settings and ...

This article provides another clear example of structuring a project for versioning. It reinforces the concept of stacking router prefixes to build your versioned URLs.

Please read the section 'Practical Section 2 - API Versioning'. Pay attention to the file structure and how app/main.py includes a router from app/api_v1/api.py with one prefix, and that file, in turn, includes the recipe router with another prefix.

After implementing this, your API documentation will clearly show the versioned endpoints, making it easy for consumers of your API to understand.

API Versioning Example in Documentation
This image shows the final result in the interactive docs. Notice how every endpoint for the recipes is clearly nested under the `/api/v1` prefix, providing an unambiguous and professional API structure.

Putting It All Together: A Concrete Example

Let's synthesize what we've learned. Assume we have our existing API, which we'll call v1. We need to introduce a v2 where the Item schema now includes a description.

1. Update Project Structure:
Organize your api directory to be version-aware.

/app
  /api
    /v1
      /endpoints
        - items.py
        - users.py
      - api.py  # V1's main router
    /v2
      /endpoints
        - items.py  # The new V2 items router
      - api.py  # V2's main router
  /schemas
    /v1
      - item.py
    /v2
      - item.py  # New schema with 'description'
  - main.py

2. Create the Version Routers:
The api.py file in each version directory groups the resources for that version.




# app/api/v1/api.py
from fastapi import APIRouter
from .endpoints import items, users

api_router = APIRouter()
api_router.include_router(items.router, prefix="/items", tags=["Items"])
api_router.include_router(users.router, prefix="/users", tags=["Users"])




# app/api/v2/api.py
from fastapi import APIRouter
from .endpoints import items  # Only items have a v2 for now

api_router = APIRouter()
api_router.include_router(items.router, prefix="/items", tags=["Items V2"])

Note how the resource-specific prefix (/items) is applied here.

3. Assemble in main.py:
Your main application file now becomes a simple assembler of versions.




# app/main.py
from fastapi import FastAPI
from app.api.v1.api import api_router as api_router_v1
from app.api.v2.api import api_router as api_router_v2

app = FastAPI()




# Include V1 router
app.include_router(api_router_v1, prefix="/api/v1")




# Include V2 router
app.include_router(api_router_v2, prefix="/api/v2")

@app.get("/")
def root():
    return {"message": "Hello World"}

The version prefix (/api/v1) is applied here. The final path is a combination of the prefixes: app prefix + resource prefix = /api/v1 + /items = /api/v1/items.

Analogy to Django/DRF:
This is very similar to how you would structure urls.py in a large Django project.

  • app.include_router(..., prefix="/api/v1") is the direct equivalent of Django's path('api/v1/', include('my_app.v1.urls')).
  • The concept of creating a dedicated URL configuration for a version (v1/api.py) and including it under a versioned path in the main entry point is identical in both frameworks.

Conclusion

In this lesson, you've learned a crucial technique for managing the lifecycle of an API. By leveraging APIRouter and a clean folder structure, you can support multiple versions of your API simultaneously, allowing you to innovate without breaking contracts with your clients.

Key Takeaways:

  • Versioning is essential for evolving an API without disrupting existing users.
  • URL-based versioning is a popular, clear, and easy-to-implement strategy.
  • FastAPI's APIRouter and the prefix argument in app.include_router are the primary tools for creating versioned URLs.
  • A scalable project structure involves organizing your routers and schemas into version-specific directories and "stacking" prefixes to construct the final endpoint paths.

Preview of the Next Lesson:

Now that we have a robust and versioned API structure, we can explore more of FastAPI's advanced capabilities. In the next lesson, we will dive into BackgroundTasks. You will learn how to perform 'fire-and-forget' operations, such as sending a confirmation email or processing a file, after the response has been sent to the client, improving your API's responsiveness.