0
0
FastAPIframework~15 mins

Startup and shutdown events in FastAPI - Deep Dive

Choose your learning style9 modes available
Overview - Startup and shutdown events
What is it?
Startup and shutdown events in FastAPI are special functions that run automatically when your web application starts or stops. They let you prepare things like database connections or clean up resources before the app begins handling requests or after it finishes. These events help your app manage important tasks that happen only once during its life cycle.
Why it matters
Without startup and shutdown events, you would have to manually manage setup and cleanup tasks inside your request handlers, which can cause repeated work, errors, or slow responses. These events make your app more efficient and reliable by handling one-time tasks at the right moments. This improves user experience and resource management in real-world applications.
Where it fits
Before learning startup and shutdown events, you should understand basic FastAPI app creation and asynchronous Python functions. After mastering these events, you can explore dependency injection, background tasks, and advanced resource management in FastAPI.
Mental Model
Core Idea
Startup and shutdown events are like the opening and closing routines of a shop, preparing everything before customers arrive and cleaning up after they leave.
Think of it like...
Imagine a coffee shop: before opening, the barista sets up the coffee machine and prepares cups (startup event). After closing, they clean the machine and lock the door (shutdown event). These routines happen once daily, not for every customer.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│   Startup     │──────▶│   Running     │──────▶│   Shutdown    │
│  (setup)      │       │   (handling   │       │  (cleanup)    │
│  Connect DB,  │       │   requests)   │       │  Close DB,    │
│  prepare res. │       │               │       │  release res. │
└───────────────┘       └───────────────┘       └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding FastAPI app lifecycle
🤔
Concept: Learn that FastAPI apps have a lifecycle with moments when the app starts and stops.
A FastAPI app runs continuously to handle web requests. But before it starts accepting requests, it can run code to prepare resources. Similarly, when the app stops, it can run code to clean up. These moments are called startup and shutdown events.
Result
You understand that the app has special moments to run setup and cleanup code separate from request handling.
Knowing the app lifecycle helps you place code where it runs once, not on every request, improving efficiency.
2
FoundationRegistering startup and shutdown events
🤔
Concept: Learn how to tell FastAPI which functions to run at startup and shutdown.
FastAPI provides decorators @app.on_event('startup') and @app.on_event('shutdown') to mark functions that run at these times. For example: from fastapi import FastAPI app = FastAPI() @app.on_event('startup') async def startup_event(): print('App is starting') @app.on_event('shutdown') async def shutdown_event(): print('App is stopping')
Result
When the app starts, 'App is starting' prints once; when it stops, 'App is stopping' prints once.
Using these decorators cleanly separates setup and cleanup code from request logic.
3
IntermediateUsing startup events for resource setup
🤔Before reading on: do you think startup events run before or after the first request? Commit to your answer.
Concept: Use startup events to create resources like database connections or caches before handling requests.
In real apps, you often need to connect to databases or initialize caches once. Startup events are perfect for this: @app.on_event('startup') async def connect_db(): app.state.db = await create_db_connection() This way, the connection is ready when requests come in.
Result
The app has a ready database connection stored in app.state.db before any request runs.
Preparing resources once at startup avoids repeated costly setup during each request.
4
IntermediateCleaning up with shutdown events
🤔Before reading on: do you think shutdown events run if the app crashes unexpectedly? Commit to your answer.
Concept: Use shutdown events to close connections and free resources when the app stops gracefully.
To avoid resource leaks, close connections or files in shutdown events: @app.on_event('shutdown') async def close_db(): await app.state.db.close() This ensures clean app exit and frees resources.
Result
Database connections close properly when the app stops, preventing leaks.
Cleaning up resources prevents errors and wasted memory after the app stops.
5
IntermediateAccessing shared state in events
🤔
Concept: Learn how to store and access shared data between startup, shutdown, and request handlers.
FastAPI's app.state lets you store data accessible anywhere: @app.on_event('startup') async def setup(): app.state.value = 42 @app.get('/') async def read_value(): return {'value': app.state.value} This shares data initialized at startup with requests.
Result
Requests can use data prepared at startup, enabling shared state.
Shared state connects lifecycle events with request handling cleanly.
6
AdvancedHandling async and sync code in events
🤔Before reading on: do you think startup events must always be async functions? Commit to your answer.
Concept: Understand how FastAPI supports both async and sync functions for events and when to use each.
Startup and shutdown functions can be async or sync. Async is preferred for I/O tasks like DB calls: @app.on_event('startup') async def async_startup(): await async_setup() @app.on_event('shutdown') def sync_shutdown(): cleanup_sync() FastAPI runs sync functions in threadpool automatically.
Result
Both async and sync event functions run correctly, allowing flexible code.
Knowing this prevents blocking the event loop and improves app responsiveness.
7
ExpertLimitations and alternatives to events
🤔Before reading on: do you think startup events run when using multiple workers in production? Commit to your answer.
Concept: Learn about challenges with startup/shutdown events in multi-worker setups and alternative patterns.
When deploying with multiple workers (e.g., Uvicorn with --workers), each worker runs startup events separately. This can cause duplicated connections or race conditions. Alternatives include: - Using lifespan context managers - External resource managers - Dependency injection with lifespan Example lifespan usage: from contextlib import asynccontextmanager @asynccontextmanager def lifespan(app): await connect_db() yield await close_db() app = FastAPI(lifespan=lifespan)
Result
You avoid duplicated startup code and manage resources safely in multi-worker environments.
Understanding deployment context is crucial to correctly manage startup/shutdown logic.
Under the Hood
FastAPI uses the ASGI lifespan protocol to detect app startup and shutdown. When the server starts, it triggers all registered startup event handlers before accepting requests. Similarly, on shutdown, it calls shutdown handlers. These handlers can be async or sync functions. FastAPI stores shared state in app.state, a simple namespace object. The event loop runs async handlers, while sync handlers run in a threadpool to avoid blocking.
Why designed this way?
This design separates one-time setup/cleanup from request handling, improving code clarity and performance. Using decorators makes it easy to register events without complex configuration. Supporting both async and sync functions ensures compatibility with various libraries. The ASGI lifespan protocol standardizes lifecycle management across frameworks, enabling interoperability.
┌───────────────┐
│  Server Start │
└──────┬────────┘
       │ triggers
┌──────▼────────┐
│ Startup Events│
│ (async/sync) │
└──────┬────────┘
       │ app ready
┌──────▼────────┐
│  Handle Req   │
│ (multiple)   │
└──────┬────────┘
       │ server stop
┌──────▼────────┐
│Shutdown Events│
│ (async/sync) │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do startup events run only once for the entire app or once per request? Commit to your answer.
Common Belief:Startup events run once for every incoming request to prepare resources.
Tap to reveal reality
Reality:Startup events run only once when the app starts, before any requests are handled.
Why it matters:Treating startup events as per-request causes inefficient repeated setup and slower responses.
Quick: Do shutdown events always run even if the app crashes? Commit to your answer.
Common Belief:Shutdown events always run no matter how the app stops, ensuring cleanup.
Tap to reveal reality
Reality:Shutdown events run only on graceful shutdown; crashes or forced kills skip them.
Why it matters:Relying on shutdown events for critical cleanup can cause resource leaks if the app crashes.
Quick: In multi-worker deployment, do startup events run once or multiple times? Commit to your answer.
Common Belief:Startup events run only once globally, even with multiple workers.
Tap to reveal reality
Reality:Each worker runs its own startup events, causing multiple executions.
Why it matters:This can lead to duplicated connections or conflicts if not handled properly.
Quick: Can you use sync functions for startup events without issues? Commit to your answer.
Common Belief:Startup events must always be async functions to work correctly.
Tap to reveal reality
Reality:FastAPI supports sync startup functions by running them in a threadpool automatically.
Why it matters:Knowing this allows using existing sync libraries without rewriting code.
Expert Zone
1
Startup events run per worker process, so shared resources must be designed for concurrency or external management.
2
Using lifespan context managers provides more control and better integration with dependency injection than simple event decorators.
3
Sync startup/shutdown functions run in threadpools, so blocking calls won't freeze the async event loop but may affect performance if overused.
When NOT to use
Avoid relying solely on startup/shutdown events for managing resources in multi-worker or serverless environments. Instead, use lifespan context managers or external resource managers like connection pools or cloud services that handle scaling and concurrency.
Production Patterns
In production, apps often use lifespan context managers combined with dependency injection to manage database connections safely. They also handle multi-worker setups by using external caches or databases that support concurrent connections. Graceful shutdown signals are used to trigger cleanup, ensuring no requests are interrupted.
Connections
Dependency Injection
Builds-on
Understanding startup events helps grasp how dependency injection can manage resource lifetimes by initializing dependencies once and sharing them across requests.
Operating System Signals
Related pattern
Shutdown events relate to OS signals like SIGTERM that tell an app to stop gracefully, linking app lifecycle to system-level process management.
Theatre Play Production
Similar pattern
Just like startup and shutdown events prepare and clean the stage before and after a play, managing app lifecycle events ensures smooth performance and cleanup.
Common Pitfalls
#1Registering blocking code directly in async startup event causing slow app start.
Wrong approach:@app.on_event('startup') async def startup(): time.sleep(5) # blocking call
Correct approach:import asyncio @app.on_event('startup') async def startup(): await asyncio.sleep(5) # non-blocking async call
Root cause:Misunderstanding that blocking calls freeze the async event loop, delaying app readiness.
#2Assuming startup event runs once globally in multi-worker deployment.
Wrong approach:# Code assumes single startup event run @app.on_event('startup') async def startup(): global_resource.connect()
Correct approach:# Use external manager or lifespan context from contextlib import asynccontextmanager @asynccontextmanager def lifespan(app): await global_resource.connect() yield await global_resource.disconnect() app = FastAPI(lifespan=lifespan)
Root cause:Not accounting for multiple worker processes each running startup events independently.
#3Not closing database connections on shutdown causing resource leaks.
Wrong approach:@app.on_event('shutdown') async def shutdown(): pass # forgot to close DB
Correct approach:@app.on_event('shutdown') async def shutdown(): await app.state.db.close()
Root cause:Overlooking the need to clean up resources leads to leaks and unstable app behavior.
Key Takeaways
Startup and shutdown events let you run code once when the app starts or stops, separating setup and cleanup from request handling.
Using these events improves efficiency by preparing resources like database connections before requests and cleaning them after.
FastAPI supports both async and sync functions for these events, running sync code safely in threadpools.
In multi-worker deployments, each worker runs startup and shutdown events, so design resource management accordingly.
For advanced control, lifespan context managers offer a better way to manage app lifecycle and resource cleanup.