FastAPI Interview Questions 41-45 (Lifespan, Docs, & Real-Time)
We are approaching the end of our comprehensive guide. By now, you know how to build, secure, and organize a standard API. But modern apps are not just request-response; they are alive.
In this lesson, we will cover the difference between blocking (sync) and non-blocking (async) code, how to run code when your server starts or stops (Lifespan), and how to build real-time features using WebSockets and Server-Sent Events (SSE).
41. Explain the difference between async and sync routes.
This is a fundamental concept in FastAPI. The framework is smart enough to handle both, but you must know when to use which.
- `async def` (Asynchronous): Use this when your code waits for something "external" that supports async/await, like:
- Reading from a database (using an async driver like asyncpg).
- Calling another API (using httpx).
- Waiting for a timer.
FastAPI runs these on the main event loop. - `def` (Synchronous): Use this when your code does "blocking" operations that don't support await, like:
- Reading a file from disk (standard `open()`).
- Heavy CPU calculations (image processing, machine learning).
- Using standard synchronous libraries (requests, pandas).
FastAPI runs these in a separate thread pool to avoid blocking the loop.
import time
import asyncio
from fastapi import FastAPI
app = FastAPI()
# Correct Async Usage
@app.get("/async-slow")
async def async_slow():
# Does NOT block other requests
await asyncio.sleep(1)
return {"message": "I yielded control!"}
# Correct Sync Usage
@app.get("/sync-slow")
def sync_slow():
# Blocks the thread, but FastAPI puts it in a threadpool
# so the main loop stays free.
time.sleep(1)
return {"message": "I ran in a thread!"}
42. What is lifespan in FastAPI and how do you use startup/shutdown events?
Historically, FastAPI used `on_event("startup")` and `on_event("shutdown")`. However, the modern and recommended way (since version 0.93.0) is using the Lifespan context manager.
Lifespan allows you to define logic that runs beforethe application starts receiving requests (like connecting to a DB or loading an ML model) and after it stops (closing connections).
from contextlib import asynccontextmanager
from fastapi import FastAPI
# Mock Database
fake_db = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
# --- Startup Logic ---
fake_db["models"] = "Loaded ML Model"
print("Startup: Connected to DB & Models Loaded")
yield # The application runs here
# --- Shutdown Logic ---
fake_db.clear()
print("Shutdown: DB Connection Closed")
# Pass the lifespan handler to the app
app = FastAPI(lifespan=lifespan)
@app.get("/")
def read_root():
return {"db_status": fake_db}
Sample Output (Terminal):
INFO: Started server process [12345]
Startup: Connected to DB & Models Loaded
INFO: Application startup complete.
... (Requests happen here) ...
INFO: Shutting down
Shutdown: DB Connection Closed
INFO: Finished server process [12345]43. How do you customize OpenAPI documentation?
FastAPI generates documentation automatically, but for a professional product, you often need to change the title, add a description, version number, or even hide certain endpoints.
from fastapi import FastAPI
app = FastAPI(
title="My Super API",
description="This is a **premium** API for enterprise users.",
version="2.5.0",
docs_url="/documentation", # Default is /docs
redoc_url=None, # Disable ReDoc
)
@app.get("/items/", tags=["items"], summary="Get all items")
def read_items():
"""
Returns a list of all available items in the warehouse.
- **limit**: The max number of items to return
"""
return [{"name": "Foo"}]
# Hide this endpoint from docs entirely
@app.get("/secret", include_in_schema=False)
def secret():
return {"msg": "Hidden"}
Visit /documentation in your browser, and you will see "My Super API" v2.5.0 with your custom description and structured route details.
44. How do you handle WebSockets in FastAPI?
WebSockets allow for full-duplex communication. Unlike HTTP (request -> response), the connection stays open. The client can send data anytime, and the server can push data anytime. This is used for chat apps or live games.
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
# 1. Accept connection
await websocket.accept()
try:
while True:
# 2. Wait for message from client
data = await websocket.receive_text()
# 3. Send response back
await websocket.send_text(f"Message text was: {data}")
except Exception:
print("Client disconnected")
Note: WebSocket connections are persistent. If you deploy this with Gunicorn/Uvicorn workers, remember that memory is not freed until the user disconnects.
45. What is Server Sent Events (SSE) and how to implement it in FastAPI?
Server-Sent Events (SSE) is a one-way communication channel where the server pushes updates to the client over a standard HTTP connection.
SSE vs WebSockets:
- WebSockets: 2-way (Chat, Games). Complex protocol.
- SSE: 1-way (Stock tickers, News feeds, Progress bars). Simple HTTP.
You implement SSE using StreamingResponse (which we learned in Q25), but with a specific media type.
import asyncio
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
app = FastAPI()
async def event_generator():
"""Yields data every second"""
for i in range(1, 6):
yield f"data: update number {i}
" # SSE Format
await asyncio.sleep(1)
@app.get("/sse")
async def sse_endpoint(request: Request):
return StreamingResponse(
event_generator(),
media_type="text/event-stream"
)
Sample Output (Client Stream):
data: update number 1
data: update number 2
data: update number 3
...