Hello! In our last lesson, we successfully built an endpoint that accepts an image, preprocesses it, and returns a real-time prediction from a machine learning model. This is a fantastic achievement and forms the core of many AI-powered services.
However, there's a subtle performance issue we haven't addressed. What if we want to perform an action after making a prediction, like logging the result to a file or sending a notification? Doing this before returning the response to the user adds unnecessary delay. The user doesn't need to wait for our server to write to a log file.
This lesson focuses on solving that exact problem. Our learning outcome is to use BackgroundTasks to run a non-critical task (e.g., logging a prediction) after a response is sent. This is a key technique for building fast, responsive APIs by deferring non-essential work.
1. What Are Background Tasks?
A background task is a function that FastAPI executes after sending the HTTP response to the client. This means the client gets its result as quickly as possible, and the server handles follow-up actions "in the background."
A great way to think about this comes from the "Using FastAPI Background Tasks" article.
Using FastAPI Background Tasks
To understand the 'why' and 'how' of background tasks, please read this short article from ApX Machine Learning. It provides a clear analogy and introduces the core concepts.
Please read the sections 'Using Background Tasks' and 'Use Cases for Background Tasks in ML APIs'. Focus on the post office analogy, which perfectly explains the concept, and take note of the practical applications for machine learning.
As you read, the key idea is decoupling the primary response from secondary actions. In the context of your goal to serve ML models, logging predictions is a perfect use case for a background task.

2. The Problem: Blocking the Response
To see why this matters, let's observe the problem firsthand. Imagine our logging function takes a few seconds to run (e.g., writing to a slow network drive or a remote database). If we run this function directly in our endpoint, the user has to wait for it to finish.
The following video demonstrates this problem clearly by using time.sleep() to simulate a slow operation.
How to Create Background Tasks in Fast API
The video 'How to Create Background Tasks in Fast API' by Pretty Printed provides a simple, effective demonstration of the problem that background tasks solve.
Please watch from 01:35 to 05:25. First, observe the section (until 03:30) where the send_email function with sleep(5) is called directly. Notice how the browser hangs, waiting for the response. Next, watch how BackgroundTasks is used to solve this. The response becomes immediate, and the 'sending email' message appears in the console after the browser has loaded.
This simple demonstration makes the benefit crystal clear: background tasks dramatically improve the user's perceived performance by making the API feel instantaneous.
3. Implementing Background Logging in our ML Endpoint
Now, let's apply this to the image prediction endpoint we built in the previous lesson. The implementation involves three simple steps:
- Import
BackgroundTasks: Get the class fromfastapi. - Add
BackgroundTasksas a dependency: Add a parameterbackground_tasks: BackgroundTasksto your path operation function. FastAPI will see this type hint and automatically inject aBackgroundTasksobject. - Add the task: Within your endpoint, call
background_tasks.add_task(your_function, arg1, arg2, ...), whereyour_functionis the function you want to run in the background.
The article "Serve Machine Learning Models via REST APIs in Under 10 Minutes" provides a perfect, concise code example of this pattern.
Serve Machine Learning Models via REST APIs in Under 10 Minutes
This article shows exactly how to integrate background logging into an ML prediction endpoint. It's a practical blueprint for what we're about to do.
Focus on the code block under 'Step 4: Create the API'. Notice how BackgroundTasks is injected into the predict function and how background_tasks.add_task is used to call the log_request function with the necessary data.
Let's modify our main.py from the previous lesson to include background logging.
import io
import pickle
import numpy as np
from PIL import Image
from datetime import datetime
# Step 1: Import BackgroundTasks
from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
# --- Load the Model (same as before) ---
try:
with open("model.pkl", "rb") as f:
model = pickle.load(f)
except FileNotFoundError:
print("Error: model.pkl not found.")
model = None
app = FastAPI(title="ML Image Classifier with Background Logging")
# --- Define our Background Task Function ---
def log_prediction(file_name: str, predicted_class: int):
"""A simple background task to log prediction details to a file."""
with open("prediction_log.txt", "a") as log_file:
timestamp = datetime.now().isoformat()
log_file.write(f"{timestamp} | FileName: {file_name} | Prediction: {predicted_class}\n")
@app.post("/predict/image")
async def predict_image(
# Step 2: Add BackgroundTasks as a dependency
background_tasks: BackgroundTasks,
file: UploadFile = File(...)
):
if not model:
raise HTTPException(status_code=500, detail="Model is not loaded")
if not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="File provided is not an image.")
contents = await file.read()
try:
pil_image = Image.open(io.BytesIO(contents)).convert("L").resize((28, 28), Image.Resampling.LANCZOS)
image_array = np.array(pil_image).flatten().reshape(1, -1)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error processing image: {e}")
prediction = model.predict(image_array)
predicted_class = int(prediction[0])
# Step 3: Add the task to be run in the background
# This function will run *after* the response below has been sent.
background_tasks.add_task(log_prediction, file.filename, predicted_class)
return {"predicted_class": predicted_class}
Now, when you run this code and send a prediction request, two things will happen:
- You will get the JSON response
{"predicted_class": ...}almost instantly. - A new file,
prediction_log.txt, will be created (or appended to), containing a line with the timestamp, filename, and prediction result.
You have successfully decoupled the non-critical logging task from the main request-response cycle!
4. Important Considerations and Limitations
While extremely useful, FastAPI's BackgroundTasks are designed for simple, non-critical operations. It's important to understand their limitations.
Using FastAPI Background Tasks
To use this feature responsibly, please read the final section of the ApX article, which covers the limitations of this approach.
Read the section 'Important Notes'. Pay close attention to the points about guaranteed execution, error handling, and resource consumption.
To summarize the key points from the article:
- No Guaranteed Execution: Background tasks run in the same process as the API. If your application crashes or restarts, any running background tasks are lost. They are not suitable for operations that must complete (e.g., processing a payment). For that, you would use a dedicated task queue like Celery.
- Error Handling: An error in a background task won't be seen by the client. You must wrap the code inside your task function (e.g.,
log_prediction) with atry...exceptblock if you want to catch and log failures. - Resource Consumption: These tasks still use your server's CPU and memory. Running many heavy tasks can still slow down your API for other users.
For our use case of simple logging, BackgroundTasks is a perfect fit.
Conclusion
In this lesson, you've learned a powerful technique for optimizing your API's responsiveness. By deferring non-critical actions, you provide a much better experience for the users of your API.
Key Takeaways:
- Background tasks run after the response is sent, preventing non-essential work from delaying the client.
- Implementation is straightforward: import
BackgroundTasks, add it as a dependency in your endpoint, and usebackground_tasks.add_task(). - This pattern is ideal for tasks like logging, sending notifications, or simple cache updates.
- For critical or resource-intensive jobs,
BackgroundTasksis not sufficient; a dedicated task queue system (like Celery) would be a more appropriate tool.
In our next lesson, we will dive deeper into the async nature of FastAPI. We'll explore the difference between def and async def route handlers and learn when to use each one to correctly handle I/O-bound and CPU-bound operations for maximum performance.