Backend line
FastAPI request lifecycle
Every click on a website follows one path, like an order through a restaurant, so you know where problems hide.
Stops explain each step. The film plays the whole ride.
- Order going in
- Side note
- Dish coming back
- Something went wrong
A customer places an order
The customer asks the website for item 42. The host at the door, a server, takes the order slip and reads it.
Next stop: The host hands it to the manager
All stops
The everyday version
Think of the front of house at a busy restaurant. Every layer has a twin there.
- The customeris the Client
Walks in and asks for a dish.
- The host at the dooris the Uvicorn
Greets you, takes the order slip, and passes it inside.
- The manageris the ServerErrorMiddleware
Stays alert and handles any kitchen fire so the whole place does not stop.
- The security deskis the Your middleware
Checks you in on the way in, and hands your things back on the way out.
- Guest relationsis the ExceptionMiddleware
Says “sorry, we are out of that” instead of leaving you confused.
- The seating hostis the Router
Reads your order and sends it to the right chef.
- The order deskis the Dependencies
Checks the order form is filled in properly before any cooking starts.
- The chefis the Your function
Receives a clean, checked order, cooks the dish, and plates only the promised parts.
- The courieris the BackgroundTasks
Delivers the receipt after you have already been served and left happy. It never makes you wait.
- A kitchen fireis a server error
The chef cannot cope. The manager steps in, and the customer only hears “something went wrong”.
Interview questions
Say the short answer first. Go deeper only if they ask.
What is ASGI, and how does Uvicorn relate to FastAPI?
ASGI is the async interface between a server and a Python app. Uvicorn is a server that calls your app.
Uvicorn turns HTTP bytes into an ASGI scope plus receive/send awaitables, then calls await app(scope, receive, send). FastAPI, through Starlette, is that callable. WSGI is sync-only and cannot do WebSockets or lifespan.
“FastAPI is the web server” or “Uvicorn is a framework”.
In what order do middlewares run?
The request goes outside-in in stack order, and the response goes inside-out.
The stack is ServerErrorMiddleware, then your middleware, then ExceptionMiddleware, then the router. Among yours, the last add_middleware call becomes outermost, which surprises people. Adding CORS last makes it wrap auth failures too.
“Middleware runs in the order I added it, both ways” or forgetting the two built-in layers.
What does def vs async def change?
def runs in a threadpool. async def runs on the event loop.
Use async def only when everything inside is awaited without blocking. A blocking call there stalls every concurrent request. A def handler with blocking I/O is safe because the loop stays free, limited by the threadpool size. Dependencies follow the same rule on their own.
“async def is always faster” or “FastAPI is multi-threaded, so blocking is fine”.
Where does validation happen, and what happens when it fails?
It happens before your function runs. A failure returns 422 automatically.
Pydantic validates params and body during dependency solving. Errors raise RequestValidationError with loc, msg and type per field. Override it with @app.exception_handler(RequestValidationError). The handler runs without ever entering your function.
“Validation happens inside my function” or “it returns 400”.
How do yield dependencies work, and when does cleanup run?
Code before yield is setup and code after is teardown. By default in current FastAPI, teardown runs after the response is sent.
Current docs say exit code of default scope="request" dependencies runs after the response is sent, while Depends(dep, scope="function") runs it before. Behaviour changed across versions (0.106 to 0.117 ran it before), so check yours. Re-raise exceptions you catch.
“Teardown always happens before the client sees anything”, or swallowing exceptions in except.
How do BackgroundTasks differ from Celery?
BackgroundTasks run in-process after the response. Celery runs in separate workers behind a broker.
BackgroundTasks have no retries, persistence or queue. If the process dies, the task is lost, and a long sync task occupies a threadpool thread. Use them for tiny fire-and-forget work like an email, and Celery or ARQ for durable or heavy work.
“They are the same thing” or “background tasks run in a separate process”.
Cheat-sheet
Commands you will actually type.
uvicorn app.main:app --reloadDev server that restarts on every code change. Not for production.
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4Multi-process production run. Workers do not share memory.
fastapi dev app/main.py
fastapi runThe FastAPI CLI: dev mode with reload, then production mode.
@asynccontextmanager
async def lifespan(app: FastAPI):
pool = await create_pool()
yield
await pool.close()
app = FastAPI(lifespan=lifespan)Startup before `yield`, shutdown after it.
async def get_db():
async with Session() as s:
yield sA yield dependency: setup, hand over the resource, then teardown.
background_tasks.add_task(send_email, to)Run this after the response is sent. No retries.
@app.exception_handler(RequestValidationError)Customize the 422 response your clients see.
app.add_middleware(CORSMiddleware, allow_origins=[...])Adds CORS. The last middleware added is the outermost.