Interview Bank · 2026

⚡ FastAPI

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.

Rapid-fire flashcards flip to check

Click a card. Answer first, in one breath, then reveal.

Path param vs query param?
Path = part of the URL path (/items/{id}), usually required. Query = after ? (/items?skip=0), usually optional with defaults.
click to flip
What does Pydantic do?
Parses and validates incoming data against typed models, coercing and rejecting bad input — then gives you a typed Python object.
click to flip
What is response_model?
A Pydantic model declaring the response shape — FastAPI filters/validates the output to it and documents it in OpenAPI.
click to flip
What is Depends / DI?
Dependency injection — FastAPI calls a function and injects its result into your route (DB sessions, auth, shared params).
click to flip
What is HTTPException?
Raise it to return an HTTP error with a status code and detail — e.g. raise HTTPException(404, "Not found").
click to flip
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.
click to flip
What is /docs?
The auto-generated Swagger UI — interactive API docs built from your types. /redoc is the alternative view.
click to flip
What is BackgroundTasks?
Runs work after the response is sent (send an email, log) without making the client wait. Inject it into the route.
click to flip
What is APIRouter?
Groups related routes into a module you include_router() on the app — keeps a big API organised with shared prefixes/tags.
click to flip
422 vs 400?
422 = FastAPI's automatic "validation failed" (body/params don't match the schema). 400 = a generic bad request you raise for business-rule errors.
click to flip

Core questions

How does FastAPI validate input?

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.

How do you declare a request body?

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.

Explain dependency injection with 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).

What is middleware and when do you use it?

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.

Why is CORS needed for the browser frontend?

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.

How do you return Pydantic models / use 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.

Sync vs async path operations — which do you write?

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.

Where does the auto-generated OpenAPI / Swagger come from?

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.

Theory deep-cuts the "why"

Why is FastAPI fast? theory

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.

ASGI vs WSGI? theory

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.

What's special about Pydantic v2's core? theory

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.

How is Depends resolved and cached? theory

For 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).

What actually happens to a sync route? theory

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.

Tricky & gotchas where candidates trip

What happens if you do a blocking/CPU call inside an async route? tricky

It 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.

Pydantic v1 vs v2 — what bites you? tricky

v2 renamed config (orm_modemodel_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.

If you inject the same dependency twice, do you get two objects? tricky

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.

Why does invalid body return 422, not 400? tricky

FastAPI 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.

How do you hide fields from the response? tricky

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.

What's the classic "forgot to await" bug? tricky

Calling 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.

What's new in 2026 say this and stand out

Why does Pydantic v2 matter? 2026

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.

What's the FastAPI CLI? 2026

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.

What modern idioms should you use? 2026

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.

Memory hooks "FastAPI = let the types do the work." Annotate, and validation, docs, and serialisation come free.
"async helps waiting, not crunching." Concurrency for I/O; for CPU you still need processes.
422 = "your shape is wrong"; 400 = "your request is wrong." Validation vs business rule.
Tie it to DocChat When asked "do you know FastAPI?", point at the build: "DocChat's backend is FastAPI — typed routes, Pydantic request/response models, a get_db dependency for the Postgres session, CORS for the Next.js frontend, and a background task for embedding uploaded docs." Concrete beats abstract.