Skip to content

Commit 7d90cd2

Browse files
committed
refactor: replace startup/shutdown events with FastAPI lifespan context manager
1 parent 9e774ca commit 7d90cd2

File tree

1 file changed

+31
-25
lines changed
  • samples/fastapi-postgres-pubsub/app

1 file changed

+31
-25
lines changed

samples/fastapi-postgres-pubsub/app/main.py

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import json
1616
import logging
1717
import os
18-
from datetime import datetime
18+
from contextlib import asynccontextmanager
19+
from datetime import datetime, timezone
1920
from pathlib import Path
2021
from typing import Any
2122

@@ -157,7 +158,8 @@ async def notification_listener(stop_event: asyncio.Event) -> None:
157158
"""Listen for ``pg_notify`` events until the app shuts down."""
158159

159160
loop = asyncio.get_running_loop()
160-
conn = await asyncpg.connect(dsn=app.state.database_dsn)
161+
database_dsn = build_database_dsn()
162+
conn = await asyncpg.connect(dsn=database_dsn)
161163

162164
def _listener(_connection: asyncpg.Connection, _pid: int, _channel: str, payload: str) -> None:
163165
loop.create_task(forward_notification(payload))
@@ -170,34 +172,22 @@ def _listener(_connection: asyncpg.Connection, _pid: int, _channel: str, payload
170172
await conn.close()
171173

172174

173-
app = FastAPI(title="FastAPI Postgres Pub/Sub Chat")
174-
app.state.connections = WebSocketRegistry()
175-
app.state.listener_stop: asyncio.Event | None = None
176-
app.state.listener_task: asyncio.Task | None = None
177-
app.state.db_pool: asyncpg.Pool | None = None
178-
app.state.database_dsn = build_database_dsn()
179-
180-
181-
def get_pool() -> asyncpg.Pool:
182-
pool = app.state.db_pool
183-
if pool is None:
184-
raise HTTPException(status_code=500, detail="Database connection not ready")
185-
return pool
186-
187-
188-
@app.on_event("startup")
189-
async def startup() -> None:
190-
pool = await asyncpg.create_pool(dsn=app.state.database_dsn, min_size=1, max_size=5)
175+
@asynccontextmanager
176+
async def lifespan(app: FastAPI):
177+
"""Manage application lifespan events."""
178+
# Startup
179+
database_dsn = build_database_dsn()
180+
pool = await asyncpg.create_pool(dsn=database_dsn, min_size=1, max_size=5)
191181
app.state.db_pool = pool
192182
await create_tables(pool)
193183

194184
stop_event = asyncio.Event()
195185
app.state.listener_stop = stop_event
196186
app.state.listener_task = asyncio.create_task(notification_listener(stop_event))
197-
198-
199-
@app.on_event("shutdown")
200-
async def shutdown() -> None:
187+
188+
yield
189+
190+
# Shutdown
201191
stop_event = app.state.listener_stop
202192
if stop_event:
203193
stop_event.set()
@@ -210,6 +200,22 @@ async def shutdown() -> None:
210200
await pool.close()
211201

212202

203+
app = FastAPI(title="FastAPI Postgres Pub/Sub Chat", lifespan=lifespan)
204+
app.state.connections = WebSocketRegistry()
205+
app.state.listener_stop: asyncio.Event | None = None
206+
app.state.listener_task: asyncio.Task | None = None
207+
app.state.db_pool: asyncpg.Pool | None = None
208+
209+
210+
def get_pool() -> asyncpg.Pool:
211+
pool = app.state.db_pool
212+
if pool is None:
213+
raise HTTPException(status_code=500, detail="Database connection not ready")
214+
return pool
215+
216+
217+
218+
213219
@app.get("/", response_class=HTMLResponse)
214220
async def index() -> HTMLResponse:
215221
if not TEMPLATE_PATH.exists():
@@ -247,4 +253,4 @@ async def healthcheck() -> dict[str, str]:
247253
pool = get_pool()
248254
async with pool.acquire() as conn:
249255
await conn.execute("SELECT 1")
250-
return {"status": "ok", "timestamp": datetime.utcnow().isoformat()}
256+
return {"status": "ok", "timestamp": datetime.now(timezone.utc).isoformat()}

0 commit comments

Comments
 (0)