Async Dependencies & Lifespan
Part of: Advanced FastAPI Patterns
Learn how to use async dependencies and the lifespan context manager to manage application startup and shutdown resources.
What You'll Learn
- Use the lifespan context manager for app startup and shutdown logic
- Create async dependencies that provide shared resources
- Understand resource management with lifespan vs deprecated on_event
- Build endpoints that consume async dependencies
Theory and Concepts
Async Dependencies & Lifespan
What You'll Learn
In this lesson you will learn how to manage application lifecycle events and create async dependencies in FastAPI. You will understand:
- How the lifespan context manager replaces the deprecated on_event decorators
- How to initialize and clean up shared resources at startup and shutdown
- How to create async dependency functions
- How to wire everything together with Depends
Theory
Application Lifecycle
Every web application needs to perform setup when it starts (connect to databases, load configuration, warm up caches) and cleanup when it stops (close connections, flush buffers). FastAPI provides the lifespan context manager for this purpose.
The Old Way: on_event (Deprecated)
Previously, FastAPI used @app.on_event("startup") and @app.on_event("shutdown") decorators:
[Code Example]
This approach has been deprecated because it separates logically related code (setup and teardown of the same resource) into two different functions.
The New Way: Lifespan Context Manager
The lifespan context manager keeps startup and shutdown logic together:
[Code Example]
Everything before yield runs at startup. Everything after yield runs at shutdown. The yield itself is where the application runs and handles requests.
Storing State with app.state
FastAPI (via Starlette) provides app.state as a place to store application-wide data:
[Code Example]
Any data attached to app.state is accessible from any request via request.app.state.
Async Dependencies
Dependencies in FastAPI can be async functions. This is useful when the dependency needs to perform async I/O:
[Code Example]
The dependency function get_db is called for every request. It reads the shared database from app.state and provides it to the endpoint.
Dependencies with Yield (Async)
You can also create async dependencies that perform cleanup after the request:
[Code Example]
This pattern is especially useful for database sessions or other resources that need to be released after each request.
Key Concepts
Lifespan Flow
1. Application starts
2. Lifespan code before yield executes (startup)
3. Application accepts and handles requests
4. Application receives shutdown signal
5. Lifespan code after yield executes (shutdown)
Dependency Injection Chain
Dependencies can depend on other dependencies, forming a chain:
[Code Example]
Best Practices
1. Always use lifespan instead of on_event decorators in new code
2. Store shared resources in app.state rather than global variables when possible
3. Keep lifespan logic minimal -- just initialization and cleanup
4. Use async dependencies when accessing async resources; use sync dependencies for simple logic
5. Handle errors in lifespan -- wrap startup code in try/except to fail gracefully
6. Use yield dependencies for resources that need per-request cleanup (like DB sessions)
Additional Resources
- FastAPI Lifespan Events
- FastAPI Dependencies
- Starlette Lifespan
- Python contextlib Documentation
Helpful Hint
Use '@asynccontextmanager async def lifespan(app)' to initialize resources at startup. Store them in app.state. Create an async dependency function that reads from app.state and yields the resource.
