Interview Bank · 2026
Type hints that validate, Pydantic that parses, and async that scales. The framework UAE teams reach for when they say "Python backend." Say each answer aloud before you reveal it.
Click a card. Answer first, in one breath, then reveal.
/items/{id}), usually required. Query = after ? (/items?skip=0), usually optional with defaults.response_model?Depends / DI?HTTPException?raise HTTPException(404, "Not found").async def vs def route?async def runs on the event loop (use with async libs/await); plain def is run in a threadpool so it won't block the loop./docs?/redoc is the alternative view.BackgroundTasks?APIRouter?include_router() on the app — keeps a big API organised with shared prefixes/tags.422 = FastAPI's automatic "validation failed" (body/params don't match the schema). 400 = a generic bad request you raise for business-rule errors.Through Python type hints + Pydantic. You annotate parameters and request bodies with types (or a Pydantic model); FastAPI reads those hints, parses the incoming JSON/query/path values, coerces them, and validates. Bad input is rejected automatically with a structured 422 — you never hand-write validation boilerplate.
Define a Pydantic model (a class subclassing BaseModel with typed fields) and add a parameter of that type to the route. FastAPI knows it came from the JSON body (not path/query), parses and validates it, and gives you a typed object: def create(item: Item):. It also appears in the OpenAPI schema automatically.
Depends (and the Annotated style).A dependency is a callable; you declare param: X = Depends(get_x) and FastAPI runs get_x() and injects its return value. Great for DB sessions, current-user/auth, and shared query logic. The modern idiom is Annotated: db: Annotated[Session, Depends(get_db)] — reusable, and works cleanly with type checkers. Dependencies can themselves have dependencies (sub-dependencies).
A function that wraps every request/response — it runs before the route and after, so it's the place for cross-cutting concerns: logging, timing, adding headers, auth gating. You add it with @app.middleware("http") or via Starlette middleware classes (CORS is one). It sees the raw request before any route does.
Browsers enforce the same-origin policy: your Next.js app on one origin can't call your API on another (different host/port) unless the API sends back Access-Control-Allow-Origin headers. FastAPI's CORSMiddleware adds them so the browser permits the request. It's a browser rule — server-to-server calls (and tools like curl) ignore it.
response_model?Return a Pydantic model (or a dict that fits one) and set response_model=Out on the decorator. FastAPI serialises it to JSON, filters the output to that schema (so you can hide internal fields like a password hash), validates it, and documents the response in OpenAPI. It's the output mirror of request validation.
Use async def when you can await async libraries (async DB driver, httpx) — it runs on the event loop and scales to many concurrent I/O-bound requests on one worker. Use plain def when your code is blocking/synchronous (a sync DB driver); FastAPI runs it in a threadpool so it doesn't freeze the loop. The mistake is doing blocking work inside an async def — that blocks everyone.
FastAPI builds an OpenAPI schema from your type hints, Pydantic models, and route metadata — served as JSON at /openapi.json — then renders it as interactive Swagger UI at /docs and ReDoc at /redoc. You get accurate, always-in-sync docs for free, and frontends can generate typed clients from it.
It's built on Starlette (a lightweight ASGI toolkit handling routing and the async server interface) and Pydantic (the data layer). The async/ASGI core handles huge numbers of concurrent I/O-bound requests on one thread, and Pydantic v2's Rust-based validation is very fast. "Fast" here means high concurrency for I/O work plus low validation overhead — not magic CPU speed.
WSGI is the old synchronous Python web interface — one request per worker, blocking. ASGI is its async successor: it supports async/await, long-lived connections, and WebSockets, letting one worker juggle many in-flight requests. FastAPI is ASGI, run by an ASGI server like Uvicorn. That async interface is what makes the concurrency possible.
Pydantic v2 rewrote its validation engine in Rust (pydantic-core), making parsing and validation several times faster than v1's pure-Python implementation. Since FastAPI validates every request and response through Pydantic, that speed-up flows straight into your API's throughput.
Depends resolved and cached? theoryFor each request, FastAPI builds a dependency graph, resolves sub-dependencies first, and calls each dependency. Crucially, the same dependency is called only once per request and its result cached — so injecting get_db into the route and into a sub-dependency yields the same session. You can opt out with Depends(fn, use_cache=False).
A plain def route would block the single-threaded event loop, so FastAPI runs it in an external threadpool (via run_in_threadpool). The loop stays free to serve other requests while your blocking code runs on a worker thread. That's why mixing sync and async safely "just works" — as long as you don't put blocking calls inside an async def.
async route? trickyIt blocks the entire event loop — every other request stalls until it finishes, because there's one loop on one thread. Fixes: use async libraries and await them, offload blocking I/O with run_in_threadpool (or write a plain def route), and push heavy CPU work to a process pool or a task queue like Celery. This is the #1 FastAPI performance bug.
v2 renamed config (orm_mode → model_config = {"from_attributes": True}), methods (.dict() → .model_dump(), .parse_obj() → .model_validate()), validators (@validator → @field_validator), and is stricter about coercion. Mixing v1-era code with v2 is a common upgrade headache — know which version the project pins.
No — the result is cached within a single request, so the same dependency returns the same value everywhere in that request (one DB session, one current-user). It's not cached across requests. Surprising if you expected a fresh instance each time; opt out with use_cache=False.
422, not 400? trickyFastAPI maps schema/validation failures to 422 Unprocessable Entity — the request was well-formed but the data is semantically wrong for the model. 400 is for generic malformed requests. If a client/test expects 400 on bad input, that's the mismatch; you can customise the exception handler if you must.
Don't put secrets in the response_model — define a separate output model. Or use response_model_exclude={"password"} / response_model_exclude_unset=True on the decorator. Returning your DB model directly often leaks internal fields; a dedicated output schema is the clean fix.
await" bug? trickyCalling an async function without await returns a coroutine object, not its result — the work never runs (and you may see a "coroutine was never awaited" warning). Symptoms: a route returns something weird/unserialisable, or a DB write silently doesn't happen. Always await async calls.
v2 is the default and its Rust core makes validation dramatically faster than v1, which directly lifts FastAPI throughput. Mentioning the v1→v2 method renames (model_dump, model_validate, from_attributes) signals you've actually shipped on the current version, not a years-old tutorial.
FastAPI ships a first-party CLI: fastapi dev runs your app with hot-reload for development, and fastapi run serves it for production — wrapping Uvicorn so you don't type the long command. It's the modern idiomatic way to start an app, replacing manual uvicorn main:app --reload in most demos.
The Annotated dependency style (Annotated[Session, Depends(get_db)]) is now the recommended pattern. And lifespan context managers (@asynccontextmanager passed as lifespan=) replace the deprecated @app.on_event("startup"/"shutdown") for opening/closing resources. Both, plus FastAPI's first-class async ecosystem (async DB drivers, httpx), mark current code.
get_db dependency for the Postgres session, CORS for the Next.js frontend, and a background task for embedding uploaded docs." Concrete beats abstract.