How to: Add a Background Task¶
Overview¶
Background tasks run asynchronously outside the request-response cycle. Your project uses prefect as the task queue.
Step-by-Step¶
1. Create the task¶
# app/worker/tasks/notifications.py
from prefect import flow
@flow(name="send-notification", log_prints=True)
async def send_notification_flow(user_id: str, message: str) -> dict:
"""Send a notification to a user."""
# Your async logic here
print(f"Sending to {user_id}: {message}")
return {"status": "sent", "user_id": user_id}
2. Call it from your API¶
# In any route or service:
import asyncio
from app.worker.tasks.notifications import send_notification_flow
# Fire and forget — runs the flow in the background (tracked in the Prefect UI)
asyncio.create_task(send_notification_flow("user_123", "Your order is ready!"))
3. Add scheduling (optional)¶
In app/worker/prefect_app.py, register a scheduled deployment in main():
from prefect.client.schemas.schedules import CronSchedule
from app.worker.tasks.notifications import send_notification_flow
deployments.append(await send_notification_flow.ato_deployment(
name="daily-digest",
parameters={"user_id": "broadcast", "message": "Daily digest"},
schedules=[CronSchedule(cron="0 9 * * *")], # Daily at 9 AM
))
4. Run the worker¶
# The prefect-server + prefect-runner containers start with `make dev`.
# To run the runner directly (registers deployments + polls for work):
uv run --directory backend python -m app.worker.prefect_app
# Prefect UI: http://localhost:4200
At most PREFECT_RUNNER_LIMIT runs execute at once (default 5) and the rest queue,
so a short schedule is cheap to add but not free: the interval decides how much
work is waiting after downtime, and every run is a process that imports the whole
application. Prefer the longest interval that still answers the question.