Sometimes, your API endpoint needs to perform an action that doesn't directly contribute to the response sent back to the client. For example, after successfully returning a prediction, you might want to log detailed information about the request and response to a separate file or database, send a notification, or trigger a cleanup process. Performing these actions before sending the response adds unnecessary latency for the client.
FastAPI provides a convenient mechanism called Background Tasks to handle exactly these situations. Background tasks are functions that are executed after the response has been sent to the client. This ensures that the client receives their response as quickly as possible, while the server handles these follow-up actions independently.
Think of it like mailing a package at the post office. Your primary goal is to get the receipt confirming the package is sent (the API response). Printing a detailed internal report about the package contents (the background task) can happen after you've left with your receipt.
FastAPI manages background tasks by running them within the same event loop (or a threadpool for blocking functions passed to background tasks), but critically, it doesn't await
their completion before sending the HTTP response.
To use background tasks, you need to:
BackgroundTasks
class from fastapi
.BackgroundTasks
. FastAPI's dependency injection system will automatically provide an instance of this class.add_task()
method on the BackgroundTasks
instance. This method takes the function to be executed as the first argument, followed by any arguments and keyword arguments that the function requires.Let's look at an example. Imagine we have a prediction endpoint, and we want to log the input features and the prediction result to a file after returning the prediction.
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
import time
import json
# Assume predict_model function exists and returns a prediction
# def predict_model(features):
# # Simulate model inference
# time.sleep(0.1)
# prediction = sum(features) * 0.5 # Dummy prediction logic
# return {"prediction": prediction}
# Assume load_model is called elsewhere to get 'model' object if needed
app = FastAPI()
class FeaturesInput(BaseModel):
sepal_length: float
sepal_width: float
petal_length: float
petal_width: float
class PredictionOutput(BaseModel):
prediction: float
def log_prediction_details(input_data: dict, output_data: dict):
"""A simple function to log prediction details to a file."""
log_entry = {"input": input_data, "output": output_data, "timestamp": time.time()}
try:
with open("prediction_log.jsonl", "a") as f:
f.write(json.dumps(log_entry) + "\n")
print("Background task: Logged prediction details.")
except Exception as e:
# Important: Log errors within the background task itself
print(f"Background task error: Failed to log prediction details: {e}")
@app.post("/predict_log_later", response_model=PredictionOutput)
async def predict_and_log(
features: FeaturesInput,
background_tasks: BackgroundTasks # Dependency Injection
):
"""
Endpoint to make a prediction and log details in the background.
"""
input_dict = features.dict()
# Replace with your actual model prediction logic
# For example: result = model.predict([list(input_dict.values())])[0]
# Using dummy prediction for demonstration
prediction_result = {"prediction": (input_dict["sepal_length"] + input_dict["petal_length"]) * 0.8}
# Add the logging function to background tasks
background_tasks.add_task(
log_prediction_details, # The function to run
input_dict, # Arguments for the function
prediction_result # More arguments
)
# The response is sent here, before log_prediction_details completes
return prediction_result
In this example:
log_prediction_details
which takes the input and output data and writes it to a file prediction_log.jsonl
.predict_and_log
endpoint depends on BackgroundTasks
.prediction_result
, we call background_tasks.add_task()
, passing our logging function and the necessary data (input_dict
, prediction_result
).prediction_result
.log_prediction_details(input_dict, prediction_result)
runs soon after, without blocking the response.While background tasks are useful, they have limitations:
try...except
blocks) and logging within the background task function itself to detect and diagnose failures.Background tasks provide a simple and effective way to improve the perceived performance of your API endpoints by deferring non-essential actions until after the response has been delivered. They are particularly useful for logging, notifications, and triggering other non-critical asynchronous operations in the context of ML model serving. Remember their limitations regarding reliability and resource usage, and choose them when the task doesn't absolutely need to complete successfully for the core operation to be considered finished.
© 2025 ApX Machine Learning