Skip to content

ManagedBackgroundTasks

ManagedBackgroundTasks is a BackgroundTasks subclass that intercepts add_task() to inject retries, status tracking, priority queuing, and task ID generation.

Because it inherits from Starlette's BackgroundTasks, isinstance checks pass and it is a drop-in replacement anywhere BackgroundTasks is used.

Guide: Task Manager covers the three injection patterns (dependency, install, and direct construction).


Constructor

from fastapi_taskflow import ManagedBackgroundTasks

ManagedBackgroundTasks(
    task_manager: TaskManager,
    background_tasks: BackgroundTasks | None = None,
)
Parameter Type Default Description
task_manager TaskManager required The task manager that holds the registry, store, and observer chain.
background_tasks BackgroundTasks \| None None The native FastAPI BackgroundTasks instance for the current request. When provided, tasks added via add_task() are appended to this list so Starlette executes them after the response is sent. When None, tasks are dispatched eagerly.

When constructed via a dependency (Depends(task_manager.get_tasks)) or install(), FastAPI passes its own native BackgroundTasks instance automatically.


Methods

add_task(func, *args, **kwargs) -> str

def add_task(
    func: Callable,
    *args: Any,
    idempotency_key: str | None = None,
    tags: dict[str, str] | None = None,
    eager: bool | None = None,
    priority: int | None = None,
    **kwargs: Any,
) -> str

Enqueues a function as a managed background task. Returns the task_id UUID string.

This overrides BackgroundTasks.add_task(), which returns None. If you are already capturing the return value in existing code, this is the only behavioural difference to be aware of.

Parameter Type Default Description
func Callable required The task function to run. Must be registered with @task_manager.task() for retries and TaskConfig settings to apply. Unregistered functions are still executed but without retry logic.
*args Any Positional arguments forwarded to func.
idempotency_key str \| None None Deduplication key. If a non-failed task with the same key already exists in the store or backend, its task_id is returned and func is not enqueued again.
tags dict[str, str] \| None None Key/value labels attached to this task. Forwarded to every LogEvent and LifecycleEvent emitted for the task.
eager bool \| None None When True, dispatch via asyncio.create_task immediately rather than waiting for the response to be sent. Overrides the decorator-level eager setting for this call only.
priority int \| None None Route through the priority queue instead of Starlette's background task list. Higher values run first; the conventional range is 1 (lowest) to 10 (highest). Overrides the decorator-level priority for this call only. Mutually exclusive with eager: when priority is set, eager is ignored.
**kwargs Any Keyword arguments forwarded to func.

Examples:

# Basic enqueue
task_id = background_tasks.add_task(send_email, address=email)

# With priority (runs before lower-priority tasks)
task_id = background_tasks.add_task(send_otp, phone, priority=9)

# Eager dispatch (starts before the response is sent)
task_id = background_tasks.add_task(notify, user_id, eager=True)

# With deduplication
task_id = background_tasks.add_task(
    generate_invoice,
    order_id,
    idempotency_key=f"invoice:{order_id}",
)

# With tags for observability
task_id = background_tasks.add_task(
    process_payment,
    amount,
    tags={"customer_id": str(customer_id), "plan": "pro"},
)

Injection patterns

Three patterns are supported. All three return a ManagedBackgroundTasks instance:

Explicit dependency (recommended for route-scoped injection):

from fastapi import Depends

@app.post("/signup")
def signup(
    email: str,
    tasks: ManagedBackgroundTasks = Depends(task_manager.get_tasks),
):
    task_id = tasks.add_task(send_welcome_email, email)
    return {"task_id": task_id}

Explicit type (after calling task_manager.install(app)):

@app.post("/signup")
def signup(email: str, background_tasks: ManagedBackgroundTasks):
    task_id = background_tasks.add_task(send_welcome_email, email)
    return {"task_id": task_id}

Zero-migration (after calling task_manager.install(app), bare BackgroundTasks receives the managed instance):

from fastapi import BackgroundTasks

@app.post("/signup")
def signup(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(send_welcome_email, email)
    # background_tasks is a ManagedBackgroundTasks instance at runtime

Direct construction for testing

You can construct ManagedBackgroundTasks directly outside of a request context. This is useful in tests:

from fastapi_taskflow import ManagedBackgroundTasks, TaskManager

tm = TaskManager()
managed = ManagedBackgroundTasks(tm)
task_id = managed.add_task(my_func, arg)

Tasks added this way are not executed by Starlette automatically because there is no active request. To execute them in a test, either call the function directly or run the task inside an asyncio event loop using the executor directly.