Top 30 Advanced Python & FastAPI Interview Questions and Answers
30 advanced multiple-choice Python & FastAPI interview questions, the deep end: internals, failure modes, and the design calls a senior engineer is expected to defend. They come from 30 bites in the Python & FastAPI library, the hardest slice of the 133 Python & FastAPI interview questions in the library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.
Python, Django, FastAPI, Flask, async Python
30 questions. Pick an answer, or open “Show the answer” to read it.
Answers are graded in your browser. Nothing is saved, and no XP or streak is earned here. The app keeps score.
Question 1 of 30
When a FastAPI endpoint awaiting asyncpg is suspended during a database query, how can Uvicorn process another incoming connection in the same worker process?
Show the answer
Answer: d · The event loop yields the coroutine at the await, registers the socket with epoll or kqueue, and schedules the new connection's coroutine on the same thread.
The correct answer describes cooperative multitasking: the event loop suspends the coroutine at await and interleaves I/O-bound tasks on a single thread. Distractor A is wrong because Uvicorn does not use multiple Python threads to handle requests; concurrency comes from the loop scheduling coroutines, not from threading or GIL behavior.
Read the full bite: How does Uvicorn use asyncio to handle thousands of concurrent connections?
Question 2 of 30
In FastAPI, why should an async database session dependency wrap yield in try and place await session.close() in finally?
Show the answer
Answer: a · It guarantees cleanup runs even if the path operation raises an exception.
A try/finally block guarantees that await session.close() runs even when the path operation raises an exception, preventing database connection leaks. The thread pool issue in distractor C is caused by using def instead of async def, not by omitting exception handling.
Question 3 of 30
In Pydantic v2, what is the runtime validation behavior of a generic wrapper field typed as T when the model is used without parametrization?
Show the answer
Answer: a · The field is validated as Any, accepting arbitrary data and generating an overly permissive schema
The card explicitly states that unparametrized TypeVars are treated as Any at validation time, yielding an overly permissive OpenAPI schema. Option C is a tempting distractor because developers often assume missing generic parameters cause runtime errors, but Pydantic v2 gracefully falls back to Any instead.
Read the full bite: Create a generic Pydantic BaseModel for API response wrappers
Question 4 of 30
A client sends GET /items?limit=foo to an endpoint with parameter limit: int. What is FastAPI's default response?
Show the answer
Answer: a · HTTP 422 Unprocessable Entity with a JSON body whose detail array contains objects with loc, msg, and type fields
FastAPI relies on Pydantic to automatically validate query parameters and returns a 422 Unprocessable Entity with a JSON detail array of objects containing loc, msg, and type fields. Option B is tempting because the status code is correct, but the body structure is actually a detailed array rather than a single string.
Read the full bite: FastAPI non-integer query param default behavior
Question 5 of 30
To accept multiple values for a single query key and allow per-item length constraints in current FastAPI, which parameter declaration should you use?
Show the answer
Answer: c · tag: Annotated[list[str], Query()] = []
Annotated[list[str], Query()] = [] is the modern pattern that separates validation metadata from the default value, enabling per-item constraints. tag: list[str] = Query(default=[]) is the outdated alternative that mixes the default with validation metadata.
Read the full bite: How do you type-hint repeated query params in FastAPI?
Question 6 of 30
Which statement accurately describes the behavior of {file_path:path} compared to {file_path} in a FastAPI route definition?
Show the answer
Answer: c · {file_path:path} uses a Starlette converter to greedily match slashes across segments while {file_path} stops at the next slash
{file_path:path} relies on Starlette's path converter to consume the rest of the URL including slashes, whereas a plain parameter matches only one segment regardless of the str type hint. Option B is wrong because a str annotation does not make routing greedy, and option A incorrectly confuses a router directive with Pydantic validation.
Question 7 of 30
In Pydantic v2, which validator configuration correctly enforces that end_date is after start_date while ensuring both fields are already parsed and coerced?
Show the answer
Answer: c · A model_validator with mode='after' that compares self.end_date and self.start_date and raises ValueError on violation
A model_validator with mode='after' receives the fully constructed instance with coerced datetime objects, making cross-field comparison type-safe and reliable. A model_validator with mode='before' is tempting but forces you to handle raw, unparsed input instead of validated types.
Read the full bite: Ensure end_date is after start_date in Pydantic
Question 8 of 30
Which of the following correctly implements a global FastAPI handler for a custom ItemNotFoundError that returns a structured 404 JSON response?
Show the answer
Answer: a · Define an async handler with signature (request: Request, exc: ItemNotFoundError), register it with @app.exception_handler, and return a JSONResponse with status_code=404 and structured content.
The correct approach registers a global handler with the Starlette-injected request parameter and returns a proper JSONResponse, centralizing error handling across all routes. Option C is tempting because it uses the correct response type, but scattering try/except blocks in every route defeats the purpose of centralized exception handling and creates unnecessary duplication.
Read the full bite: Implement a custom exception handler to catch ItemNotFoundError and return 404
Question 9 of 30
Which statement accurately describes the execution guarantee of a FastAPI BackgroundTask scheduled after returning a 201 response?
Show the answer
Answer: c · It runs in the same worker process and is lost if that process exits before completion
BackgroundTasks execute in the same Starlette worker process with no persistence or retry logic, so a process crash or restart loses the task. Option B is tempting because candidates often mistake this convenience mechanism for a distributed queue like Celery, but Starlette does not persist tasks across restarts.
Read the full bite: How would you use BackgroundTasks to run work after returning a 201?
Question 10 of 30
When verifying a webhook signature in FastAPI by awaiting request.body(), why will a Pydantic model parameter declared in the same endpoint fail to populate?
Show the answer
Answer: a · The HTTP body stream can only be consumed once, so reading it exhausts the source before the model parser runs.
FastAPI relies on Starlette, where the HTTP body is a single-use async stream; once await request.body() consumes it, no bytes remain for the framework's automatic parser to build the Pydantic model. Option D is tempting because declaration order matters in some dependency systems, but the stream is exhausted regardless of parameter order.
Read the full bite: Access raw request bytes in FastAPI for webhook verification
Question 11 of 30
You need a FastAPI dependency that combines a path parameter and a request header. What is the correct implementation pattern?
Show the answer
Answer: d · Annotate each parameter in the dependency function with its source (e.g., Path, Header) and declare the function as a dependency with Depends().
FastAPI inspects dependency signatures using the same resolution engine as endpoints, so annotating parameters with Path, Header, or similar and using Depends() lets the framework inject them automatically. Option B is tempting for developers familiar with lower-level frameworks, but manually parsing Request bypasses validation and defeats the purpose of FastAPI's dependency injection system.
Read the full bite: How would you implement a dependency requiring multi-source parameters?
Question 12 of 30
You move a dependency that loads a 2GB ML model from a global FastAPI app dependency to a single path operation. What is the actual effect?
Show the answer
Answer: d · It runs only when that specific path is hit, yet still reloads the model on every request to that path.
Path-local dependencies are resolved per-request, so the expensive load repeats on every call to that route; moving it merely limits blast radius. Distractor D is wrong because limiting the dependency to one route does not eliminate per-request overhead, which is why expensive initialization belongs in a lifespan event or cached singleton.
Read the full bite: How does lifecycle differ for global vs path operation dependencies?
Question 13 of 30
In FastAPI, when a dependency callable declares its own parameters using Depends, how does the framework resolve them at runtime?
Show the answer
Answer: d · It builds a dependency graph, resolving sub-dependencies first and injecting their results into parent dependencies.
FastAPI's injection solver treats Depends declarations as nodes in a dependency graph, recursively resolving sub-dependencies and feeding their outputs into parent callables before the endpoint executes. This is fundamentally different from middleware, which intercepts requests at the ASGI layer rather than performing parameter-level resolution.
Read the full bite: Explain the internal role of the Depends class
Question 14 of 30
Why is using pkgutil.iter_modules to scan app/routers/ safer than directly calling importlib.import_module on every .py file during dynamic router discovery?
Show the answer
Answer: a · It avoids executing top-level module code during the scanning phase, reducing the risk of side effects before validation.
pkgutil.iter_modules lists candidate modules without executing their code, isolating the discovery phase from import side effects. Distractor D is tempting because startup latency is a real concern, but scanning alone does not defer imports or eliminate latency; lazy-loading requires a separate mechanism.
Read the full bite: How do you dynamically discover and register FastAPI routers from a directory?
Question 15 of 30
When scaling FastAPI beyond simple routes, what is the recommended pattern for combining framework-native Depends with a formal DI container?
Show the answer
Answer: a · Bootstrap a container during startup for singletons and deep graphs, then use thin Depends wrappers to resolve or build request-scoped services.
The card recommends a hybrid approach: a formal container initializes singletons and deep graphs during startup, while thin Depends callables bridge request-scoped resources into FastAPI. Option C is the red flag of using Depends for everything, which scatters construction logic and hurts testability, while C creates noisy transitive coupling in routes and D sacrifices explicit lifecycle management.
Read the full bite: How do you manage service lifecycle with FastAPI Depends versus formal DI?
Question 16 of 30
How do you override a dependency for a single APIRouter without affecting the main FastAPI application?
Show the answer
Answer: b · Instantiate a sub-application, set its dependency_overrides, include the router, and mount it in the main app
Only FastAPI application instances maintain a dependency_overrides dictionary; APIRouter is merely a route grouping mechanism with no such registry. Mounting a sub-application with its own overrides isolates the mock without leaking state into the main app or relying on fragile global mutations.
Read the full bite: Override a FastAPI dependency at the APIRouter level
Question 17 of 30
When a background task created with asyncio.create_task() raises an unhandled exception and the caller never awaits it, what is the immediate consequence?
Show the answer
Answer: a · The exception is stored in the Task object, the event loop continues running, and the creator can retrieve it later via task.exception() or by awaiting.
The correct answer captures the asyncio Task exception lifecycle: failures are stored in the Task object, the loop continues, and the creator retrieves them later via task.exception() or awaiting. Option C is tempting because unhandled exceptions often crash threads or processes, but asyncio Tasks isolate failures and do not stop the event loop.
Read the full bite: Unhandled exception in asyncio.create_task(): consequence and detection
Question 18 of 30
After catching CancelledError in an asyncio task to perform async cleanup, why must you re-raise the exception before returning?
Show the answer
Answer: b · Because the event loop uses the exception to finalize the task and wake any joiners.
Re-raising CancelledError lets the event loop mark the task as done and unblock any coroutines awaiting it; swallowing it breaks the contract and can leak the task. Option C is tempting because the exception may propagate to an awaiter, but the critical reason to re-raise is to satisfy the event loop's finalization contract, not to interrupt the caller.
Read the full bite: How do you gracefully cancel and clean up an asyncio task?
Question 19 of 30
When using run_in_executor in asyncio, why must contextvars be manually propagated into the worker thread?
Show the answer
Answer: c · The executor runs on a different OS thread, so the event loop's task context is not automatically present there
The executor runs on a different OS thread, so the event loop's task context is not automatically present there. The coroutine-local distractor is wrong because contextvars propagate across await and task boundaries, not just a single coroutine object.
Read the full bite: Why are contextvars better than threading.local in async Python?
Question 20 of 30
When atomically creating an order and updating inventory in FastAPI with SQLAlchemy 2.0, which approach correctly guarantees atomicity and early error detection?
Show the answer
Answer: c · Wrap both operations in async with session.begin(), call session.flush() after the inventory update, and let exceptions propagate out of the block.
Keeping both writes inside a single session.begin() block and flushing after the inventory update surfaces constraint errors immediately while still allowing the context manager to roll back automatically when exceptions propagate. Committing after the order creation is a common mistake that splits the operation into two transactions, risking orphaned orders if the inventory update subsequently fails.
Read the full bite: How do you atomically create an order and update inventory?
Question 21 of 30
Running 4 Uvicorn workers each with pool_size 5 and max_overflow 10, what is the maximum number of connections the database could see at peak?
Show the answer
Answer: c · 60
Each worker has its own pool, so peak equals workers times (pool_size plus max_overflow), or 4 times 15, which is 60. Answering 15 ignores that pools are per-process and not shared.
Read the full bite: SQLAlchemy connection pooling across Uvicorn workers
Question 22 of 30
Why is deleting a JWT from the browser's storage insufficient to truly revoke it before expiry?
Show the answer
Answer: d · A JWT is self-validating, so any copy of it remains accepted until it expires unless the server tracks revocation state
A JWT carries its own signature and expiry, so any retained copy stays valid until expiration unless the server maintains a denylist or revocable refresh token. Client-side deletion does nothing for a token an attacker already captured.
Question 23 of 30
Which PyJWT decode configuration correctly prevents algorithm confusion and cross-service token replay?
Show the answer
Answer: a · Pass explicit algorithms, verify aud and iss, and require exp via options
Explicit algorithm whitelisting prevents algorithm confusion per RFC 8725, while verifying aud, iss, and requiring exp stops cross-service replay and missing claim abuse. Option D enables algorithm confusion by trusting the header, while D dangerously disables critical validations.
Read the full bite: What JWT claims must you validate beyond the signature?
Question 24 of 30
Which architectural component is specifically required when returning HTTP 202 for a multi-minute PDF job because BackgroundTasks alone is inadequate?
Show the answer
Answer: d · A persistent message broker such as Redis or RabbitMQ that survives restarts and enables retries.
BackgroundTasks is same-process and non-persistent, so a broker is required for durability and retries across restarts. A thread pool or more Uvicorn workers does not prevent job loss on crash, and async libraries do not solve the architectural durability gap.
Read the full bite: Why is FastAPI BackgroundTasks poor for multi-minute PDF generation?
Question 25 of 30
When middleware manages a request-scoped database session in FastAPI, what is the correct way to handle database work inside a BackgroundTask?
Show the answer
Answer: d · Pass only primitive IDs to the BackgroundTask and have it acquire a fresh, independent session.
BackgroundTasks execute after the response is sent, so the middleware has already closed the request session and returned its connection to the pool; giving the task only serializable IDs and letting it open a new session avoids detached-instance or closed-connection errors. Reusing the request session fails because the task runs outside the request-response lifecycle.
Read the full bite: Design DB transaction middleware and identify the background-task pitfall
Question 26 of 30
When unit-testing a FastAPI endpoint that enqueues a background email task, which strategy correctly verifies the enqueueing logic without executing side effects?
Show the answer
Answer: a · Override the BackgroundTasks dependency with a mock and assert add_task was called with the exact callable and arguments
The correct approach is to mock the injected BackgroundTasks instance and verify add_task receives the right callable and arguments, keeping enqueueing verification separate from task logic. Patching the email function directly (Option C) is tempting but fails to verify the endpoint actually wired the task through the framework's background machinery.
Read the full bite: How do you test FastAPI background tasks are enqueued correctly?
Question 27 of 30
Which pytest pattern correctly tests the full lifecycle of a FastAPI WebSocket endpoint using TestClient?
Show the answer
Answer: a · Use a standard def test, wrap client.websocket_connect in a with statement, and call receive_json inside the block.
TestClient drives the async app from a synchronous test, so you use a standard def function with a websocket_connect context manager to exercise the full message lifecycle. Using async def is unnecessary because TestClient internally handles the event loop, and skipping the with statement leaves the connection improperly managed.
Read the full bite: How do you test a FastAPI WebSocket endpoint and lifecycle with pytest?
Question 28 of 30
Which strategy correctly isolates a router-level dependency override when testing a FastAPI application with multiple routers?
Show the answer
Answer: a · Apply the override to app.dependency_overrides in a fixture and clear it during teardown.
FastAPI stores dependency overrides globally on the app instance, so the correct approach is to use app.dependency_overrides inside a fixture and clear it during teardown to prevent state leaks. Option B is tempting because the dependency is declared on the router, but APIRouter does not have its own override registry.
Question 29 of 30
Which approach lets you add a custom x-internal-id to a single FastAPI path operation schema without manual JSON edits or global overrides?
Show the answer
Answer: a · Pass the field in the path operation decorator's OpenAPI Extra dictionary so FastAPI merges it into that route's operation
FastAPI's OpenAPI Extra dictionary is the built-in per-route hook that merges custom keys directly into a specific operation object. Overriding the global schema generator is a common misconception because it is unnecessary for a single route and forces manual reimplementation of default schema generation.
Read the full bite: Add a custom x- field to a FastAPI path operation schema
Question 30 of 30
You need to disable FastAPI's interactive documentation routes in production while keeping the raw OpenAPI schema available for internal consumers. What is the proper way to achieve this?
Show the answer
Answer: c · Pass docs_url=None and redoc_url=None to the FastAPI constructor while leaving openapi_url at its default
The correct approach uses FastAPI's native constructor parameters to prevent the UI routes from being registered entirely while preserving the raw schema endpoint. Requiring authentication is wrong because it keeps the routes exposed rather than disabling them, and middleware still leaves the routes in the routing table.
Read the full bite: How do you disable FastAPI docs but keep the OpenAPI schema?
Could you explain these out loud?
That is what an interview actually tests. Tezvyn gives you questions like these with what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.