FastAPI Task Definition
Introduction
Background tasks in FastAPI provide a powerful way to perform operations after the response has been sent to the client. This allows your API to remain responsive while handling time-consuming operations in the background. In this tutorial, we'll explore how to define background tasks in FastAPI, understand their purpose, and implement them effectively in your applications.
FastAPI's task definition mechanism leverages Python's async capabilities without requiring complex setup of separate worker processes or task queues for simple use cases. Let's dive in and learn how these tasks work.
Understanding Background Tasks in FastAPI
Background tasks in FastAPI are operations that run in the background after your API has already sent the response to the client. This is particularly useful for:
- Sending notification emails
- Processing uploaded files
- Updating database records
- Performing cleanup operations
- Triggering long-running computations
The key benefit is that these operations don't delay the response to the user, improving the perceived performance of your API.
Basic Task Definition
The BackgroundTasks
Class
FastAPI provides the BackgroundTasks
class from the fastapi
module that allows you to add and run background tasks.
Here's a simple example of how to define a background task:
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def write_log(message: str):
with open("log.txt", "a") as log_file:
log_file.write(f"{message}\n")
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
# Add task to be run in the background
background_tasks.add_task(write_log, f"Notification sent to {email}")
return {"message": "Notification sent in the background"}
In this example:
- We define a simple function
write_log
that writes a message to a log file - We inject the
BackgroundTasks
instance into our endpoint - We add our function to the background tasks using
background_tasks.add_task()
- The API returns a response immediately
- After the response is sent, FastAPI executes the
write_log
function
Parameters to Background Tasks
You can pass parameters to your background task functions:
def process_item(item_id: int, action: str):
# Process the item (e.g., database operations)
print(f"Processing item {item_id} with action: {action}")
@app.post("/items/{item_id}")
async def create_item(
item_id: int,
action: str = "create",
background_tasks: BackgroundTasks
):
# Add the task with parameters
background_tasks.add_task(process_item, item_id, action)
return {"message": f"Item {item_id} being processed in the background"}
Multiple Task Definition
You can add multiple background tasks that will be executed in the order they were added:
def update_stats(item_id: int):
# Update statistics for the item
print(f"Updating stats for item {item_id}")
def notify_admin(item_id: int):
# Send notification to admin
print(f"Notifying admin about item {item_id}")
@app.post("/items/{item_id}/process")
async def process_item(item_id: int, background_tasks: BackgroundTasks):
# Add multiple tasks
background_tasks.add_task(update_stats, item_id)
background_tasks.add_task(notify_admin, item_id)
return {"message": "Item processing started"}
Task Definition with Dependencies
One of the powerful features of FastAPI is how seamlessly background tasks integrate with the dependency injection system. You can define background tasks in dependencies:
from fastapi import Depends
def get_notification_service(background_tasks: BackgroundTasks):
def send_email(email: str, message: str):
background_tasks.add_task(
send_email_background, email=email, message=message
)
return {"send_email": send_email}
def send_email_background(email: str, message: str):
# Code that actually sends the email
print(f"Sending email to {email}: {message}")
@app.post("/users/")
async def create_user(
email: str,
notification_service: dict = Depends(get_notification_service)
):
# Create user logic here...
# Send welcome email in the background
notification_service["send_email"](
email=email,
message="Welcome to our service!"
)
return {"message": "User created successfully"}
Real-World Example: Image Processing
Let's explore a practical example where background tasks can significantly improve user experience. Consider an API endpoint that allows users to upload images that need to be processed (resized, filtered, etc.):
from fastapi import FastAPI, File, UploadFile, BackgroundTasks
import aiofiles
import time
from pathlib import Path
app = FastAPI()
async def save_upload_file(upload_file: UploadFile, destination: Path):
async with aiofiles.open(destination, 'wb') as out_file:
content = await upload_file.read()
await out_file.write(content)
async def process_image(image_path: Path):
# Simulate image processing that takes time
time.sleep(5) # In a real app, this would be actual image processing
print(f"Processed image: {image_path}")
# Update database with processed image path or other logic
# ...
@app.post("/upload-image/")
async def upload_image(
background_tasks: BackgroundTasks,
file: UploadFile = File(...)
):
# Create a unique file name
file_name = f"uploaded_{file.filename}"
file_path = Path(f"uploads/{file_name}")
# Save the file
await save_upload_file(file, file_path)
# Add image processing to background tasks
background_tasks.add_task(process_image, file_path)
return {
"filename": file_name,
"status": "File uploaded successfully. Processing started in background."
}
In this example:
- The user uploads an image
- We save the image immediately
- We add the time-consuming image processing to a background task
- We return a response to the user right away
- The image is processed in the background after the response is sent
This approach provides a much better user experience as the user doesn't have to wait for processing to complete before receiving a response.
Error Handling in Background Tasks
It's important to handle errors properly in background tasks. Since these tasks run after the response is sent, any exceptions won't be automatically handled by FastAPI's exception handlers.
import logging
logger = logging.getLogger("background_tasks")
def process_with_error_handling(item_id: int):
try:
# Potentially problematic code
if item_id % 2 == 0:
# Simulate an error for even IDs
raise ValueError(f"Cannot process even item ID: {item_id}")
print(f"Successfully processed item {item_id}")
except Exception as e:
# Log the error
logger.error(f"Background task error: {e}", exc_info=True)
# You might want to store the error in a database or retry the task
@app.post("/process/{item_id}")
async def process_endpoint(item_id: int, background_tasks: BackgroundTasks):
background_tasks.add_task(process_with_error_handling, item_id)
return {"message": "Processing started"}
Best Practices for Task Definition
When defining background tasks in FastAPI, keep these best practices in mind:
-
Keep tasks small and focused: Each background task should do one thing well.
-
Handle exceptions: Always wrap your task logic in try-except blocks to prevent unhandled exceptions.
-
Consider task duration: For tasks that take more than a few seconds, consider using a proper task queue like Celery or RQ instead.
-
Be mindful of resources: Background tasks run in the same process as your API, so they share resources. Too many intensive background tasks could slow down your API.
-
Don't rely on request context: The request might be completed by the time your background task runs, so don't rely on request-specific data unless you've captured it as parameters.
-
Log task progress: Add appropriate logging to track the execution of background tasks.
import logging
logger = logging.getLogger("api")
def background_task_with_logging(item_id: int):
logger.info(f"Starting background processing of item {item_id}")
# Your processing code here
logger.info(f"Completed background processing of item {item_id}")
@app.post("/items/{item_id}")
async def process_item(item_id: int, background_tasks: BackgroundTasks):
logger.info(f"Received request to process item {item_id}")
background_tasks.add_task(background_task_with_logging, item_id)
return {"status": "Processing started"}
Limitations of FastAPI Background Tasks
While FastAPI's background tasks are convenient, they have limitations:
- They run in the same process as your API server
- If your server restarts, any pending background tasks are lost
- They are not distributed across multiple workers
- They don't provide built-in retries, monitoring, or task status tracking
For more complex requirements, you might need to consider full-featured task queues like Celery, Redis Queue (RQ), or other asynchronous job systems.
Summary
FastAPI task definition provides a simple yet powerful way to execute code asynchronously after sending a response to the client. This improves API responsiveness and user experience by moving time-consuming operations to the background.
You've learned:
- How to define background tasks using the
BackgroundTasks
class - How to pass parameters to background tasks
- Defining multiple tasks that run in sequence
- Integrating tasks with FastAPI's dependency injection system
- Handling errors in background tasks
- Best practices for defining effective background tasks
In simple scenarios, FastAPI's built-in background tasks are often sufficient. For more complex requirements, you might need to integrate with dedicated task queue solutions.
Additional Resources and Exercises
Resources
- FastAPI Official Documentation on Background Tasks
- Starlette Background Tasks (FastAPI builds on Starlette)
- Python asyncio Documentation
Exercises
-
Email Notification System: Create an endpoint that registers a user and sends a welcome email in the background.
-
File Processing Pipeline: Build an API that accepts file uploads and processes them through multiple background tasks (e.g., validation, transformation, storage).
-
Retry Mechanism: Implement a simple retry mechanism for background tasks that might fail.
-
Dashboard: Create a simple dashboard that tracks the status of background tasks by storing their status in a database.
-
Task Prioritization: Implement a system that manages background tasks with different priority levels.
By mastering background task definition in FastAPI, you'll be able to build more responsive and efficient web applications that provide excellent user experience even when complex processing is required.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)