FastAPI Interview Questions
Real FastAPI interview questions on framework comparisons, validation, DI, async, auth, and deployment.
A curated set of FastAPI interview questions, ordered roughly from fundamentals to more advanced — the kind you'll actually be asked in real screens and on-sites.
Fundamentals & comparisons
Q: How would you compare FastAPI, Flask, and Django for a new API project? Flask is a minimal, unopinionated framework you assemble yourself — a good fit for a small API with simple needs. Django is batteries-included (ORM, admin, auth) and suits larger, more conventional applications, including ones that serve full server-rendered pages, not just an API. FastAPI sits between them for API-first work specifically: it's async-first, uses Python type hints for automatic request validation, and generates interactive API documentation for free — trade-offs Flask and Django weren't originally designed around, since both predate the ASGI/async ecosystem FastAPI is built on.
Q: Why does FastAPI rely so heavily on Python type hints? Type hints give FastAPI (via Pydantic) enough information to validate incoming request data, serialize outgoing responses, and generate the OpenAPI schema — all from the same source of truth as your function signatures. This removes the need for a separate validation schema or hand-maintained API spec that could silently drift out of sync with the actual code, and it means editor autocomplete and static type checking "just work" on request/response data too.
Validation & data shape
Q: What actually validates an incoming JSON body in FastAPI?
Pydantic. When an endpoint parameter is annotated with a BaseModel subclass, FastAPI parses the request body as JSON, validates every field against the model's declared types and constraints, and raises a 422 Unprocessable Entity with a detailed per-field error message before your endpoint function ever executes if validation fails.
Q: What's the difference between a request body model and a response_model?
The request model defines and validates what a client is allowed to send in; the response_model defines and enforces what the API is allowed to send back, independent of the internal object an endpoint happens to return. This matters most for security and API contract clarity — a response_model can strip fields (like a password hash) that exist on the underlying data but should never appear in a response.
Dependency injection
Q: What does Depends() actually do, mechanically?
It tells FastAPI to call the given function before the endpoint runs, and pass whatever it returns (or yields) into the endpoint as a normal argument. Dependencies can themselves depend on other dependencies, forming a resolved chain — FastAPI handles calling all of them in order, and if a dependency uses yield, the code after the yield runs as cleanup once the request is done, even if the endpoint raised an exception.
Q: Give a real example of what you'd put behind Depends() in a production API.
The two most common cases are a database session (a dependency that opens a session, yields it, and closes it in a finally-equivalent block after the request) and authentication (a dependency that reads an Authorization header, validates the token, and either returns the current user or raises an HTTPException(401)). Both let every endpoint that needs them simply declare db=Depends(get_db) or user=Depends(get_current_user) instead of duplicating that logic.
Async fundamentals
Q: When does using async def for a path operation actually help performance, versus plain def?
It helps when the endpoint awaits genuine async I/O — an async database driver, an httpx.AsyncClient call to another service — because the event loop can serve other requests while waiting instead of that thread sitting idle. It does not help, and can actively hurt, if you call blocking/synchronous code inside an async def function: that blocks the single event loop thread and stalls every other concurrent request. A plain def endpoint calling blocking code is actually the safer choice, since FastAPI automatically runs it in a separate thread pool instead of on the event loop.
Authentication
Q: How does FastAPI validate a bearer token on a protected route, and what actually enforces authentication?
OAuth2PasswordBearer only extracts the token string from the Authorization header (and documents that requirement on /docs) — it does not itself validate anything. The actual enforcement lives in a dependency like get_current_user, which decodes and verifies the JWT's signature and expiry (typically with jose.jwt.decode), raising an HTTPException(401) if that fails. Any route that declares current_user = Depends(get_current_user) gets that check for free, with no logic duplicated across routes.
Testing
Q: How do you test a FastAPI endpoint that depends on a database, without hitting a real database?
Use app.dependency_overrides to replace the real dependency (e.g. get_db) with a fake that yields an in-memory or stub session, then drive the endpoint through TestClient as normal. Because the endpoint only ever depends on whatever Depends(get_db) resolves to, the substitution is invisible to the endpoint's own code — the same pattern used for mocking authentication or any other external dependency in tests.
Deployment
Q: Why is uvicorn main:app --reload unsuitable for production, and what replaces it?
--reload adds file-watching overhead purely for development convenience, and a single Uvicorn process only uses one CPU core regardless of load. Production deployments instead run multiple worker processes — either Uvicorn's own --workers N flag or, more commonly, Gunicorn managing several Uvicorn worker processes (--worker-class uvicorn.workers.UvicornWorker) — which adds mature process supervision (automatic restarts, graceful reloads) on top of what Uvicorn provides alone.