Metadata-Version: 2.4
Name: modern-di-fastapi
Version: 4.2.2
Summary: modern-di integration for FastAPI
Keywords: dependency-injection,di,ioc-container,modern-di,fastapi,python,asyncio
Author: Artur Shiriev
Author-email: Artur Shiriev <me@shiriev.ru>
License-Expression: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
Classifier: Typing :: Typed
Classifier: Topic :: Software Development :: Libraries
Requires-Dist: fastapi>=0.100,<1
Requires-Dist: modern-di>=4.2,<5
Requires-Dist: anyio>=3.6.2,<5
Requires-Python: >=3.11, <4
Project-URL: Homepage, https://modern-di.modern-python.org
Project-URL: Documentation, https://modern-di.modern-python.org/integrations/fastapi/
Project-URL: Repository, https://github.com/modern-python/modern-di-fastapi
Project-URL: Issues, https://github.com/modern-python/modern-di-fastapi/issues
Project-URL: Changelog, https://github.com/modern-python/modern-di-fastapi/releases
Description-Content-Type: text/markdown

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)"  srcset="https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/modern-di-fastapi/lockup-dark.svg">
    <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/modern-di-fastapi/lockup-light.svg">
    <img alt="modern-di-fastapi" src="https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/modern-di-fastapi/lockup.png" width="420">
  </picture>
</p>

[![PyPI version](https://img.shields.io/pypi/v/modern-di-fastapi.svg)](https://pypi.org/project/modern-di-fastapi/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/modern-di-fastapi.svg)](https://pypi.org/project/modern-di-fastapi/)
[![Downloads](https://static.pepy.tech/badge/modern-di-fastapi/month)](https://pepy.tech/projects/modern-di-fastapi)
[![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/modern-python/modern-di-fastapi/actions/workflows/ci.yml)
[![CI](https://github.com/modern-python/modern-di-fastapi/actions/workflows/ci.yml/badge.svg)](https://github.com/modern-python/modern-di-fastapi/actions/workflows/ci.yml)
[![License](https://img.shields.io/github/license/modern-python/modern-di-fastapi.svg)](https://github.com/modern-python/modern-di-fastapi/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/modern-python/modern-di-fastapi)](https://github.com/modern-python/modern-di-fastapi/stargazers)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)

[modern-di](https://github.com/modern-python/modern-di) integration for [FastAPI](https://fastapi.tiangolo.com).

Full guide: [FastAPI integration docs](https://modern-di.modern-python.org/integrations/fastapi/)

Usage example: [fastapi-sqlalchemy-template](https://github.com/modern-python/fastapi-sqlalchemy-template)

## Installation

```bash
uv add modern-di-fastapi      # or: pip install modern-di-fastapi
```

## Usage

`setup_di` stores the container on the app and opens and closes it with the app lifespan. The container opens before your lifespan's startup code and closes after its shutdown code, so both can resolve `APP`-scoped dependencies. `FromDI` resolves a provider (or type) into a route parameter. It builds the per-connection child container on demand through `Depends(build_di_container)`, so only routes that use `FromDI` get one.

```python
import dataclasses

import fastapi
from modern_di import Container, Group, Scope, providers
from modern_di_fastapi import FromDI, setup_di


@dataclasses.dataclass(kw_only=True)
class Settings:
    debug: bool = True


@dataclasses.dataclass(kw_only=True)
class UserService:
    settings: Settings  # auto-injected by type


class Dependencies(Group):
    settings = providers.Factory(scope=Scope.APP, creator=Settings)
    user_service = providers.Factory(scope=Scope.REQUEST, creator=UserService)


app = fastapi.FastAPI()
container = Container(groups=[Dependencies])
setup_di(app, container)
container.validate()  # optional fail-fast; must come after setup_di registers its providers


@app.get("/")
async def index(user_service: UserService = FromDI(Dependencies.user_service)) -> dict[str, bool]:
    return {"debug": user_service.settings.debug}
```

The framework `Request` / `WebSocket` are resolvable within DI via the pre-built `fastapi_request_provider` / `fastapi_websocket_provider` context providers.

An app mounted under another with `app.mount` can call `setup_di` with its own container, and its routes resolve from that container. Starlette runs only the outermost app's lifespan, so the outer app's lifespan also opens and closes the root container of every mounted app that called `setup_di`, including apps under `Host` routes, apps mounted with `Mount(..., middleware=[...])`, and apps mounted more than one level deep. It looks for them at startup, so mounts added after `setup_di` are included. Each container is opened once at startup, before your lifespan code, and closed once at shutdown, after it. The mounted app's own lifespan function still does not run. A mounted app whose `setup_di` was called with `manage_lifespan=False` keeps its root out of this. The flag covers only the container of the app it is passed to, and a mounted app's own `setup_di` call decides for its container: an outer app with `manage_lifespan=False` still opens and closes the containers of mounted apps that kept the default.

### Sharing a container

Pass `manage_lifespan=False` when something else owns the root container, for example one container shared by several apps. `setup_di` then never opens or closes the root, and you do. It still registers its providers and still opens and closes a child container for each connection.

```python
import uvicorn

container = Container(groups=[Dependencies])
setup_di(app, container, manage_lifespan=False)


async def main() -> None:
    async with container:
        await uvicorn.Server(uvicorn.Config(app)).serve()
```

## API

| Symbol | Description |
|---|---|
| `setup_di(app, container, *, manage_lifespan=True)` | Stores the container on `app.state` and composes the container's open/close into the app lifespan (opening it before any existing `lifespan=` starts and closing it after that lifespan's shutdown code). With `manage_lifespan=False` the lifespan leaves the root alone and you open and close it |
| `FromDI(dependency, *, use_cache=True)` | FastAPI `Depends` that resolves a provider (or type) from the per-connection child container. Raises `RuntimeError` naming `setup_di` when a request reaches it without `setup_di` called |
| `fetch_di_container(app)` | Returns the app-scoped container from `app.state`. Raises `RuntimeError` naming `setup_di` when called without `setup_di` called |
| `build_di_container(connection)` | FastAPI `Depends` callable that yields the per-connection child container: `REQUEST` scope for an HTTP request, `SESSION` scope for a WebSocket |
| `fastapi_request_provider` | `ContextProvider` for the current `fastapi.Request` (`REQUEST` scope) |
| `fastapi_websocket_provider` | `ContextProvider` for the current `fastapi.WebSocket` (`SESSION` scope) |

## Links

- [PyPI](https://pypi.org/project/modern-di-fastapi)
- [License](https://github.com/modern-python/modern-di-fastapi/blob/main/LICENSE)

## Part of `modern-python`

Built on [`modern-di`](https://github.com/modern-python/modern-di), a dependency-injection framework with an IoC container and scopes.

Browse the full list of templates and libraries in
[`modern-python`](https://github.com/modern-python); the org profile has the categorized index.
