FastAPI, Flask or Django
TrapAn async view in Flask still holds a worker for the whole request.
async def runs on the event loop, and plain def runs in a threadpool. A blocking call inside async def stalls every request on that worker.Depends() gives a route what it needs, such as a session or the current user. FastAPI calls each dependency once per request, and tests can swap it for a fake.TrapAn async view in Flask still holds a worker for the whole request.
async def app(scope, receive, send)TrapThe speed helps I/O-bound work. CPU-heavy Python in your endpoint runs at normal Python speed.
With await, one loop serves A, B and C together. A blocking call in async def stops all three. In def, the call holds one thread and the others run.
requestsdef handler():httpxasync def handler():TipCPU-heavy work needs a process pool or a task queue, because of the GIL.
Traptime.sleep(1) inside async def freezes the whole worker for a second.
Asked in interviews
AskedWhat is the difference between sync and async path operations?@app.on_event("startup") TrapEach worker runs its own lifespan, so four workers load four copies of a model.
Asked in interviews
AskedWhat are lifespan events in FastAPI and when do you use them?uvicorn main:app --reload@app.post("/items") @app.api_route("/x", methods=["GET", "POST"])TipA method with no function on a known path gets a 405, with no code from you.
item_id: int = Path(..., gt=0)Annotated[int, Path(gt=0)]class Color(str, Enum):q: str | None = NoneWith no default, it's required.limit: int = Query(10, le=100)Query(min_length=3, pattern="^a")?tags=a&tags=btags: list[str] = Query([])TipA list needs an explicit Query, or FastAPI reads it as the body.
TrapRoutes match in declaration order, so /users/me must come before /.
status_code=201On the decorator.response.status_code = 404Declare response: Response.raise HTTPException(404)TrapSetting response. doesn't stop your function. Raising HTTPException does.
Asked in interviews
AskedHow do you customize HTTP status codes in responses?/docs/redoc/openapi.jsoninclude_in_schema=False TipThe docs come from the code that validates requests, so they stay in sync.
Asked in interviews
AskedHow does FastAPI generate interactive API documentation?APIRouter(prefix="/users") app.include_router(router) app.include_router(v1, prefix="/v1")api_version: str = Header("1.0")It hides the version from logs and links.TipMake a new version only for breaking changes, and flag old routes with deprecated=True.
TrapNever import the app into a router module. That avoids circular imports.
is_offer: bool = Falseaddress: Addresshomes: list[Address]tags: list[str] = []It's safe, because Pydantic copies it for each instance.TipA missing city fails with its full path, ["body", "address", "city"].
@field_validator("price") @model_validator(mode="after") mode="before"TipRaise ValueError to fail, and FastAPI returns a 422.
TrapA field validator must return the value. An after-mode model validator must return self.
Asked in interviews
AskedHow do you implement custom validators in Pydantic models?class Item(BaseModel): @dataclassTrapPass a string where a dataclass expects an int and it stores the string. Its hints only help tools like mypy.
Asked in interviews
AskedWhat is the difference between Pydantic's BaseModel and dataclasses?response_model=UserResponse class UserCreate(UserBase): class UserResponse(UserBase): PATCH sentmodel_dump(exclude_unset=True) Tip-> UserResponse does the same job. If you set both, response_model wins.
.dict().model_dump()orm_modefrom_attributes=True class Configmodel_config@validator@field_validator @root_validator@model_validator TipFastAPI dropped v1 in 0.126.0. Since 0.135.2, it needs Pydantic 2.9+.
TrapMost old names only warn. pydantic. and a bare @root_validator raise errors.
db: Session = Depends(get_db)dependencies=[Depends(auth)] DependsCurrentUser = Annotated[dict, Depends(get_user)] TipThe finally runs even when the endpoint raises.
TrapPass the function, not its result. Write Depends(get_db), not Depends(get_.
def get_user(db=Depends(get_db)): Depends(fn, use_cache=False) @lru_cacheTrap@lru_cache is per process. Never cache a session or the current user this way.
OAuth2PasswordBearer(tokenUrl="token") jwt.decode(token, KEY, algorithms=[ALG]) TipThe sub claim names the user and exp sets the expiry. JWTs are stateless, so keep them short-lived.
TrapAlways pass an explicit algorithms list. Otherwise a client could choose the algorithm.
Asked in interviews
AskedHow do you implement JWT authentication in FastAPI?APIKeyHeader(name="X-API-Key") secrets.compare_digest(a, b)TipStore only a hash of each key, and give each client its own.
TrapA plain != stops at the first mismatch, so an attacker can guess a key by timing responses.
Asked in interviews
AskedHow do you implement API key authentication?def require_role(role): It returns a dependency that raises 403.Depends(require_role("admin")) 401 or 403401 means not authenticated. 403 means known, but without the role.TrapA role in a JWT stays valid until the token expires. Load it from the database for sensitive actions.
Asked in interviews
AskedHow do you implement role-based access control?add_middleware(CORSMiddleware) allow_origins=[url] allow_credentials=True Then none of the lists may be ["*"].PUT.TipBy default no origin is allowed, and GET is the only method.
TrapCORS only controls what browsers let pages read. curl ignores it, so the API still needs authentication.
Asked in interviews
AskedHow do you handle CORS in FastAPI?create_engine(URL) sessionmaker(bind=engine) async defcreate_async_engine(URL) env.py at your modelstarget_metadata=Base.metadata alembic revision --autogeneratealembic upgrade headAs a deploy step, not at app startup.TipThe standard session blocks, so use it from plain def routes.
TrapAutogenerate writes a rename as a drop and an add, which loses data. Review every script.
with db.begin():.with_for_update() pool_size=20max_overflow=30pool_size + max_overflow.TipLock rows in a fixed order to avoid deadlocks. Keep transactions short.
TrapA generic except Exception turns your 400 into a 500. Catch HTTPException first.
@app.middleware("http") await call_next(request) X-Request-ID and return it as a header.TipDon't log request bodies, which often hold passwords or tokens.
TrapThe last middleware you add runs first, because each new one wraps the ones before it.
@app.exception_handler(ValueError) 422 errors to change their shapeRequestValidationError ExceptionLog it, and return a generic 500.TipHandle Starlette's HTTPException to catch its 404 for unknown routes too.
TrapHTTPException raised in middleware reaches the client as a 500. Return a JSONResponse there.
Asked in interviews
AskedHow do you handle errors globally in FastAPI?file: UploadFile = File(...)Needs python-multipart.await file.read(1024 * 1024)StreamingResponse(gen()) FileResponse(path) TipUploadFile spools to disk past a size limit. A bytes parameter reads it all into memory.
TrapA file request is form data, so it can't also carry a JSON body. Never trust the client's filename.
tasks.add_task(send_email, email)Declare tasks: BackgroundTasks.TrapTasks run in the same process. If the worker crashes, pending tasks are lost, with no retries.
Asked in interviews
AskedHow do you implement background tasks in FastAPI?await websocket.accept() await websocket.receive_text() except WebSocketDisconnect: raise WebSocketException(1008) TipTo broadcast across workers, add a shared channel such as Redis pub/sub.
TrapBrowsers can't set custom headers on a WebSocket. Send the token in a query parameter or a cookie.
Asked in interviews
AskedHow do you implement WebSockets in FastAPI?client = TestClient(app)with TestClient(app) as client:app.dependency_overrides[get_db] = fakeapp.dependency_overrides = {}TipTest functions stay plain def, even for async endpoints.
TrapThe key must be the same function object the routes use. Import get_db, don't redefine it.
jwt.encode(claims, KEY, algorithm=ALG)TrapA made-up string such as "valid_token" fails the real jwt.decode.
Asked in interviews
AskedHow do you test authentication in FastAPI?uvicorn main:app --workers 4gunicorn main:app -w 4 -k uvicorn_worker.UvicornWorker The old uvicorn.workers module is deprecated.--workers.TipWrite the Dockerfile's CMD in exec form, so the server receives shutdown signals.
Trap--reload is for development only.
Asked in interviews
AskedHow do you deploy FastAPI applications?.env file, with typesfrom pydantic_settings import BaseSettingsclass Settings(BaseSettings): Depends(get_settings) With @lru_cache on it.TipA missing required value raises a validation error. Call get_settings() at startup, so a bad deploy fails early.
Asked in interviews
AskedHow do you handle environment configuration in FastAPI?db.execute(text("SELECT 1"))Readiness. A 503 stops traffic to it.TrapIf liveness tests the database, a short database outage restarts every container at once.
Asked in interviews
AskedHow do you implement health checks in FastAPI?p95 latency, database time and event loop lag.GZipMiddlewaregenerate_latest() p95 latency and the 5xx rate.TipA response model is the fastest way to serialize, because Pydantic's Rust core writes the JSON.
TrapLabel by route template, not request.. A series per user ID overloads Prometheus.
JSONResponse(status_code=429) await redis.setex(key, 600, value)TipA token bucket refills at a fixed rate, so it allows short bursts.
TrapLeave the user ID out of the cache key and one user sees another's response.
Nothing matches. Try a shorter word, or clear the search.
With Pro, you can:
Already have Pro? Sign in