FastAPI Dependency Injection Drill
A four-question drill on FastAPI's Depends system: caching, sub-dependencies, override patterns, and yield-based teardown. Aimed at developers preparing for a Python web backend interview.
By CodeSnatch
April 3, 2026
·
Updated August 13, 2026
330 views
8
4.3 (13)
A FastAPI endpoint declares Depends(get_current_user) and so does a helper sub-dependency on the same request. Is get_current_user invoked once or twice per request, and what controls that?
Examples
Example 1:
Input: Request to /me with both route and require_admin depending on Depends(get_current_user)
Output: 'resolving user' prints once
Explanation: FastAPI caches each dependency once per request keyed by the callable identity.Example 2:
Input: Replace one with Depends(get_current_user, use_cache=False)
Output: 'resolving user' prints twice
Explanation: Opting out of per-request caching forces a fresh evaluation for that dependency.from fastapi import Depends, FastAPI
app = FastAPI()
def get_current_user():
print('resolving user')
return {'id': 1}
def require_admin(user = Depends(get_current_user)):
return user
@app.get('/me')
def me(user = Depends(get_current_user), admin = Depends(require_admin)):
return {'user': user, 'admin': admin}A test wants to stub out the database session used by an endpoint without touching the endpoint's signature. Show the override pattern FastAPI gives you for this, and explain why monkeypatching the module-level function would be the wrong move.
Examples
Example 1:
Input: app.dependency_overrides[get_db] = fake_db; client.get('/items')
Output: [{"id": 1, "name": "a"}]
Explanation: The override intercepts get_db wherever it appears in the dependency tree.Example 2:
Input: app.dependency_overrides.clear() after the test
Output: subsequent tests use the real dependency
Explanation: Clearing the overrides restores production wiring so tests do not leak state.Example 3:
Input: Monkeypatch module.get_db instead of using dependency_overrides
Output: route still calls the original function
Explanation: FastAPI captured the original callable at route registration time; module replacement does not redirect it.from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
def get_db():
raise RuntimeError('real DB not available in tests')
@app.get('/items')
def list_items(db = Depends(get_db)):
return db.query('SELECT * FROM items')A teammate writes a DB session dependency using yield and is worried about the order in which teardown runs when an exception is raised inside the endpoint. Walk through what FastAPI guarantees about yield-based dependencies and exceptions, and write a session dependency that rolls back on failure and commits on success.
Examples
Example 1:
Input: POST /orders returns 200
Output: session.committed == True; session.close() runs in finally
Explanation: No exception was raised, so the post-yield commit path runs before the finally cleanup.Example 2:
Input: Handler raises HTTPException(400)
Output: session.rolled_back == True; the 400 response is still returned
Explanation: The exception flows into the dependency generator, triggers rollback, is re-raised, and FastAPI turns it into the response.from fastapi import Depends, FastAPI
app = FastAPI()
def get_session():
# TODO: yield a session, commit on clean exit, rollback if the endpoint raised.
raise NotImplementedErrorTwo endpoints both need pagination params (page, size). Junior reviewers keep copy-pasting the same Query(...) declarations into every handler. Show the cleanest FastAPI pattern that keeps validation, defaults, and OpenAPI docs intact without losing autocomplete on the endpoint.
Examples
Example 1:
Input: GET /users?page=2&size=50
Output: { "page": 2, "size": 50, "kind": "users" }
Explanation: Pagination is resolved once by FastAPI inspecting its __init__ signature; the handler receives a typed object.Example 2:
Input: GET /users?page=0
Output: 422 Unprocessable Entity
Explanation: ge=1 on the Query annotation fails validation before the handler runs, and OpenAPI documents the constraint.from fastapi import FastAPI, Query
app = FastAPI()
@app.get('/users')
def list_users(page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100)):
...
@app.get('/orders')
def list_orders(page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100)):
...