As your FastAPI application for serving machine learning models grows, managing all your API endpoints within a single Python file (like main.py
) quickly becomes difficult. Code becomes harder to navigate, maintain, and debug. Different logical parts of your API, such as prediction endpoints, status checks, or model management routes, get tangled together.
FastAPI provides a clean solution for this: APIRouter
. Think of an APIRouter
as a mini FastAPI application. It allows you to group related path operations (endpoints) together, typically in separate Python modules, and then connect them back to your main FastAPI
application instance. This modular approach is fundamental for building larger, well-structured services.
An APIRouter
works much like the main FastAPI
application object. You can define path operations (using decorators like @router.get
, @router.post
, etc.) on an APIRouter
instance just as you would on an app
instance.
Let's see how to use it. Imagine you want to separate your model prediction endpoints into their own module.
First, create a new file, for example, routers/predictions.py
. Inside this file, you import APIRouter
and create an instance:
# routers/predictions.py
from fastapi import APIRouter
from pydantic import BaseModel
# Define your input/output Pydantic models if needed
class PredictionInput(BaseModel):
feature1: float
feature2: str
# ... other features
class PredictionOutput(BaseModel):
prediction: float
probability: float | None = None
# Create the router instance
router = APIRouter()
# Define path operations on the router instance
@router.post("/make", response_model=PredictionOutput)
async def make_prediction(data: PredictionInput):
"""
Accepts input data and returns a model prediction.
(Actual model loading and inference logic would go here)
"""
# Placeholder for model inference logic
prediction_value = 1.0 # Replace with actual model.predict(...)
probability_value = 0.85 # Replace if your model provides probabilities
# Here you would typically:
# 1. Preprocess input data (data.dict())
# 2. Get prediction from your loaded ML model
# 3. Postprocess the result if necessary
# 4. Return the structured output
return PredictionOutput(prediction=prediction_value, probability=probability_value)
@router.get("/info")
async def get_model_info():
"""
Returns basic information about the prediction model.
"""
return {"model_name": "ExamplePredictor", "version": "1.0"}
# You can add more prediction-related endpoints here...
Notice that we use @router.post
and @router.get
instead of @app.post
and @app.get
. The function signatures and Pydantic model usage remain exactly the same.
Now, you need to tell your main FastAPI
application about this router. In your main.py
(or wherever your main FastAPI
instance is defined), you import the router object and use the app.include_router()
method.
# main.py
from fastapi import FastAPI
from routers import predictions # Import the module containing your router
# Create the main FastAPI application instance
app = FastAPI(
title="ML Model Prediction Service",
description="API for serving predictions from an ML model.",
version="0.1.0"
)
# Include the predictions router
app.include_router(predictions.router)
@app.get("/")
async def read_root():
return {"message": "Welcome to the Prediction API!"}
# You could include other routers here as well
# from routers import monitoring
# app.include_router(monitoring.router)
With this setup, the endpoints defined in routers/predictions.py
(/make
and /info
) are now part of your main application. FastAPI handles the routing internally. When a request comes in for /make
or /info
, it will be directed to the corresponding function within the predictions
router.
The include_router
method offers useful parameters for better organization, especially for API documentation and URL structure.
prefix
: Adds a path prefix to all routes defined in the router. This is excellent for grouping endpoints under a common path segment.tags
: Assigns tags to all routes in the router. These tags are used to group operations in the automatic API documentation (like Swagger UI).Let's modify our main.py
to use these:
# main.py (updated)
from fastapi import FastAPI
from routers import predictions
app = FastAPI(
title="ML Model Prediction Service",
description="API for serving predictions from an ML model.",
version="0.1.0"
)
# Include the predictions router with a prefix and tags
app.include_router(
predictions.router,
prefix="/predict", # All routes in predictions.router now start with /predict
tags=["Predictions"] # Group these endpoints under 'Predictions' in docs
)
@app.get("/")
async def read_root():
return {"message": "Welcome to the Prediction API!"}
Now, the endpoint previously accessed at /make
will be available at /predict/make
, and /info
will be at /predict/info
. In your interactive API documentation (usually found at /docs
), you'll see these endpoints neatly grouped under the "Predictions" tag.
Diagram showing the main application including the prediction router module. The
include_router
call specifies a/predict
prefix and assigns the 'Predictions' tag.
Using APIRouter
is a significant step towards building maintainable and scalable FastAPI applications. It allows you to:
/api/v1/predict/...
).As your ML API grows, adopting routers early will save considerable effort in the long run, forming the backbone of a well-organized project structure.
© 2025 ApX Machine Learning