Interview Q&A · 99 questions · 21 answered publicly
FastAPI Interview Questions
Ordered by how often they're asked. Sign in free to see every answer, rate your confidence and run timed mock interviews.
-
Most asked What is FastAPI and what makes it different from Flask or Django?
Summary
FastAPI is a modern ASGI Python web framework built on Starlette and Pydantic. It uses type hints to drive request validation, serialization, dependency injection and automatic OpenAPI docs.
Longer Answer
Where Flask is a minimal WSGI microframework and Django is a batteries-included full-stack framework, FastAPI focuses on APIs. Declaring
def get_item(item_id: int, q: str | None = None)is enough for FastAPI to parse, validate and document the parameters. It supportsasync defnatively, generates Swagger UI (/docs) and ReDoc (/redoc), and is among the fastest Python frameworks thanks to Starlette and Uvicorn.Code Example
from fastapi import FastAPI app = FastAPI(title="Inventory API") @app.get("/items/{item_id}") async def read_item(item_id: int, q: str | None = None) -> dict: return {"item_id": item_id, "q": q} # run: uvicorn main:app --reloadKey Points
- ASGI, built on Starlette + Pydantic
- Type hints → validation, docs and editor support
- Native async support
- Automatic OpenAPI/Swagger docs
- Dependency injection system via
Depends
Comparison / Tradeoffs
FastAPI Flask Django Interface ASGI WSGI (async views limited) WSGI/ASGI Validation Pydantic built-in Extensions Forms/DRF serializers Docs Automatic OpenAPI Extensions DRF + extensions -
Most asked How does FastAPI distinguish path, query and body parameters?
Summary
Parameters named in the path template are path parameters; simple-typed parameters not in the path are query parameters; parameters typed as Pydantic models are read from the JSON body.
Longer Answer
You can make the source explicit with
Path(),Query(),Body(),Header(),Cookie()andForm(), usually viaAnnotatedfor metadata like validation constraints and descriptions. Optional query params have defaults (q: str | None = None). Lists from repeated query keys needQuery():tags: Annotated[list[str], Query()] = [].Code Example
from typing import Annotated from fastapi import FastAPI, Path, Query from pydantic import BaseModel app = FastAPI() class ItemUpdate(BaseModel): name: str price: float @app.put("/items/{item_id}") async def update_item( item_id: Annotated[int, Path(ge=1)], item: ItemUpdate, notify: Annotated[bool, Query()] = False, ): return {"id": item_id, **item.model_dump(), "notify": notify}Key Points
- Path template names → path params
- Scalars otherwise → query params
- Pydantic models → body
Annotated[int, Query(ge=1)]adds constraints
-
Most asked How does FastAPI use Pydantic for request validation?
Summary
Request bodies, query models and settings are declared as Pydantic
BaseModelclasses. FastAPI parses incoming data into them, validates types and constraints, and returns a 422 response with detailed errors on failure.Longer Answer
Pydantic v2 (with its Rust core,
pydantic-core) coerces compatible input (e.g.,'3'→3in lax mode), enforces constraints (Field(gt=0, max_length=50)), and supports custom validators (@field_validator,@model_validator). Errors include the location (body.price), message and type, which clients can map to form fields.Code Example
from pydantic import BaseModel, Field, field_validator, EmailStr class SignUp(BaseModel): email: EmailStr password: str = Field(min_length=12) age: int = Field(ge=18) @field_validator("password") @classmethod def has_digit(cls, v: str) -> str: if not any(c.isdigit() for c in v): raise ValueError("password must contain a digit") return vKey Points
- Models declared with type hints and
Field()constraints - Invalid input → automatic 422 response
@field_validator/@model_validatorfor custom rules- Strict mode disables coercion
- Models declared with type hints and
-
Most asked What does `response_model` (or a return annotation) do in FastAPI?
Summary
It declares the output schema: FastAPI validates and filters the returned data through that model, so extra fields (like password hashes) are excluded, and it documents the response in OpenAPI.
Longer Answer
You can use a return type annotation (
-> UserOut) orresponse_model=UserOut(needed when the function returns something else, like an ORM object or dict). Options likeresponse_model_exclude_unset=Trueomit fields not explicitly set. Separate input and output models (UserCreatewith a password,UserOutwithout) to avoid leaking data.Code Example
class UserCreate(BaseModel): email: EmailStr password: str class UserOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int email: EmailStr @app.post("/users", response_model=UserOut, status_code=201) async def create_user(data: UserCreate, db: DbSession): user = await users.create(db, data) # ORM object with hashed_password return user # hashed_password is filtered outKey Points
- Filters output to the declared schema
- Documents responses in OpenAPI
- Separate In/Out models
- Return ORM objects with
from_attributes=True
-
Most asked How does dependency injection with `Depends` work in FastAPI?
Summary
You declare a parameter as
Annotated[T, Depends(provider)]; FastAPI calls the provider per request (resolving its own dependencies recursively), caches the result within that request, and injects it.Longer Answer
Dependencies can be functions, async functions, classes or generators (
yielddependencies for setup/teardown like DB sessions). They can declare their own query params, headers or other dependencies, which appear in the OpenAPI docs. Override them in tests withapp.dependency_overrides[provider] = fake. Router- or app-leveldependencies=[Depends(verify_token)]run for all routes without injecting a value.Code Example
from typing import Annotated from fastapi import Depends, Header, HTTPException async def get_api_key(x_api_key: Annotated[str, Header()]) -> str: if x_api_key != settings.api_key: raise HTTPException(status_code=401, detail="Invalid API key") return x_api_key ApiKey = Annotated[str, Depends(get_api_key)] @app.get("/reports") async def reports(_: ApiKey): return {"ok": True}Key Points
- Declarative, per-request resolution
- Sub-dependencies resolved recursively
- Cached per request by default (
use_cache=Falseto disable) dependency_overridesfor testing
-
Most asked How do `yield` dependencies work and why are they used for database sessions?
Summary
A dependency that
yields provides the yielded value to the endpoint and runs the code afteryieldonce the request is done, which suits opening and closing DB sessions or transactions.Longer Answer
FastAPI wraps the generator like a context manager. Exceptions raised in the endpoint propagate into the dependency at the
yield, so you can roll back inexcept. Exactly when teardown runs relative to sending the response has changed between FastAPI versions, so commit explicitly in your service/endpoint (so commit errors reach the client) and let background tasks open their own sessions instead of reusing the request's.Code Example
from collections.abc import AsyncIterator from sqlalchemy.ext.asyncio import AsyncSession async def get_db() -> AsyncIterator[AsyncSession]: async with SessionLocal() as session: try: yield session # endpoint/service calls `await session.commit()` except Exception: await session.rollback() raise DbSession = Annotated[AsyncSession, Depends(get_db)]Key Points
- Code before
yield= setup - Code after
yield= teardown - Catch exceptions to roll back
- Background tasks should open their own sessions
- Code before
-
Most asked When should an endpoint be `async def` versus plain `def` in FastAPI?
Summary
Use
async defwhen the endpoint only awaits async I/O (async DB drivers, httpx). Use plaindefwhen it calls blocking libraries, because FastAPI runsdefendpoints in a threadpool so they don't block the event loop.Longer Answer
The danger is calling blocking code (e.g.,
requests.get,time.sleep, sync SQLAlchemy, CPU-heavy work) insideasync def. That freezes the event loop for every concurrent request. Options: switch to async libraries, make the endpointdef, or offload withawait run_in_threadpool(func)/anyio.to_thread.run_sync. CPU-bound work belongs in a process pool or a task queue.Code Example
import httpx from fastapi.concurrency import run_in_threadpool @app.get("/weather") async def weather(city: str): async with httpx.AsyncClient(timeout=5) as client: # non-blocking r = await client.get(f"https://api.example.com/w?city={city}") return r.json() @app.get("/legacy") def legacy_report(): # sync def: runs in threadpool return blocking_sdk.generate() @app.get("/mixed") async def mixed(): data = await run_in_threadpool(blocking_sdk.generate) return dataKey Points
async def+ only non-blocking awaitsdefendpoints run in a threadpool (default ~40 threads)- Never block inside
async def - CPU-bound → process pool / Celery
-
Most asked How do you implement OAuth2 password flow with JWT in FastAPI?
Summary
Use
OAuth2PasswordBearer(tokenUrl="token")as a dependency that extracts the Bearer token, issue signed JWTs (PyJWT) at the token endpoint after verifying the password hash, and decode/verify the token in aget_current_userdependency.Longer Answer
Hash passwords with
pwdlib/passlib(Argon2 or bcrypt). The token includessub(user ID) andexp.get_current_userdecodes with a pinned algorithm, loads the user, and raises 401 with aWWW-Authenticate: Bearerheader on failure. Layer authorization with dependencies likerequire_role('admin')or OAuth2 scopes viaSecurity().Code Example
import jwt from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") @app.post("/token") async def login(form: Annotated[OAuth2PasswordRequestForm, Depends()], db: DbSession): user = await authenticate(db, form.username, form.password) if not user: raise HTTPException(401, "Incorrect credentials", headers={"WWW-Authenticate": "Bearer"}) token = jwt.encode({"sub": str(user.id), "exp": utcnow() + timedelta(minutes=30)}, SECRET, algorithm="HS256") return {"access_token": token, "token_type": "bearer"} async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: DbSession) -> User: try: payload = jwt.decode(token, SECRET, algorithms=["HS256"]) except jwt.InvalidTokenError: raise HTTPException(401, "Invalid token", headers={"WWW-Authenticate": "Bearer"}) user = await db.get(User, int(payload["sub"])) if user is None: raise HTTPException(401, "User not found") return userKey Points
OAuth2PasswordBearerextracts the token- Sign JWTs with expiry; pin algorithms
get_current_userdependency- Role/scope checks as composable dependencies
-
Most asked How do you use SQLAlchemy 2.0 async with FastAPI?
Summary
Create an async engine (
create_async_enginewithasyncpg), anasync_sessionmaker, provide a session per request via ayielddependency, and useselect()withawait session.execute()orsession.scalars().Longer Answer
Set
expire_on_commit=Falseso returned objects stay usable after commit. Lazy loading doesn't work implicitly in async (it raisesMissingGreenlet), so eager-load relationships withselectinload()/joinedload(). Configure pool size based on worker count and the DB's max connections. Use Alembic for migrations.Code Example
from sqlalchemy import select from sqlalchemy.orm import selectinload from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker engine = create_async_engine(settings.database_url, pool_size=10, max_overflow=5) SessionLocal = async_sessionmaker(engine, expire_on_commit=False) @router.get("/{user_id}/orders") async def user_orders(user_id: int, db: DbSession): stmt = select(User).options(selectinload(User.orders)).where(User.id == user_id) user = (await db.scalars(stmt)).one_or_none() if user is None: raise HTTPException(404) return user.ordersKey Points
create_async_engine('postgresql+asyncpg://...')- Session per request via
yielddependency expire_on_commit=False- Eager loading (
selectinload) instead of lazy loads
-
Most asked Walk through what happens to a request in FastAPI, from the ASGI server to the response.
Summary
The ASGI server calls the app; Starlette's outer ServerErrorMiddleware wraps your middleware (the last added runs first), then ExceptionMiddleware applies your exception handlers; the router matches the path; FastAPI resolves dependencies (once per request by default) and validates parameters and the body with Pydantic (422 on failure); the endpoint runs (async def on the event loop, def in a thread pool); the return value is serialised through the response model; dependency exit code and background tasks run afterwards.
Longer Answer
Knowing the layers explains common surprises: an HTTPException raised in middleware becomes a 500 because it's outside the exception layer; validation errors never reach your endpoint; a blocking call in an async def endpoint stalls every request on the event loop. Exactly when dependencies with yield run their exit code relative to sending the response has changed between FastAPI versions, so check the documentation for your version.
Key Points
- ServerErrorMiddleware → your middleware → ExceptionMiddleware → router.
- Dependencies resolved and cached per request.
- Pydantic validation, 422 on failure.
- async def on the loop, def in a thread pool.
Deep dive: Request Lifecycle course: FastAPI and Starlette: Middleware, Exceptions and Dependencies
-
Most asked What is ASGI and how does it differ from WSGI?
Summary
WSGI is the synchronous Python interface between servers and apps (one request per call). ASGI is its async successor, supporting async request handling, WebSockets, HTTP/2 and long-lived connections.
Longer Answer
A WSGI app is a callable
app(environ, start_response)that blocks a worker thread per request. An ASGI app isasync def app(scope, receive, send), so one event loop can multiplex thousands of concurrent connections. FastAPI/Starlette are ASGI apps served by Uvicorn, Hypercorn or Granian.Key Points
- WSGI: sync, request/response only
- ASGI: async, WebSockets, lifespan events
- Servers: Uvicorn, Hypercorn, Granian
- ASGI apps can still run sync code in a threadpool
Deep dive: Request Lifecycle course: WSGI, ASGI and the Event Loop
-
Most asked How do you run startup and shutdown logic in FastAPI?
Summary
Use the
lifespanparameter with an async context manager: code beforeyieldruns at startup (create DB pools, HTTP clients, load ML models) and code after runs at shutdown.Longer Answer
The older
@app.on_event("startup")handlers are deprecated. Lifespan can yield a state dict, available asrequest.state(Starlette 0.26+), or store objects onapp.state. With multiple Uvicorn workers, lifespan runs once per worker process.Code Example
from contextlib import asynccontextmanager import httpx @asynccontextmanager async def lifespan(app: FastAPI): app.state.http = httpx.AsyncClient(timeout=10) app.state.model = load_model("model.bin") yield await app.state.http.aclose() app = FastAPI(lifespan=lifespan)Key Points
lifespan=async context manager- Replaces deprecated
on_event - Runs per worker process
- Good place for shared clients/pools
-
Most asked What are `BackgroundTasks` and when should you use a real task queue instead?
Summary
BackgroundTasksruns small functions after the response is sent, in the same process; for heavy, slow, retryable or critical jobs, use a queue like Celery, RQ, Dramatiq, arq or a managed queue.Longer Answer
Background tasks share the server process: if the worker crashes or restarts, tasks are lost; there are no retries, scheduling or visibility; and CPU-heavy tasks steal capacity from requests. They're fine for sending a notification email or writing an audit log.
Code Example
from fastapi import BackgroundTasks def send_welcome_email(email: str) -> None: mailer.send(email, "Welcome!") @app.post("/signup", status_code=201) async def signup(data: SignUp, tasks: BackgroundTasks): user = await create_user(data) tasks.add_task(send_welcome_email, user.email) return {"id": user.id}Key Points
- Runs after response, same process
- No persistence or retries
- Good for small fire-and-forget work
- Use Celery/arq/RQ for durable jobs
-
Most asked How do you add middleware and configure CORS in FastAPI?
Summary
Add middleware with
app.add_middleware(...)(e.g.,CORSMiddleware,GZipMiddleware,TrustedHostMiddleware) or write HTTP middleware with@app.middleware("http").Longer Answer
CORS: list exact
allow_origins(avoid*withallow_credentials=True; browsers reject it). Middleware wraps every request, so it suits timing, request IDs and security headers. For performance-critical code, pure ASGI middleware avoids the overhead ofBaseHTTPMiddleware. Middleware order matters: the last added is the outermost.Code Example
import time, uuid from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["https://app.example.com"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.middleware("http") async def add_request_id(request, call_next): request_id = request.headers.get("x-request-id", str(uuid.uuid4())) start = time.perf_counter() response = await call_next(request) response.headers["x-request-id"] = request_id response.headers["server-timing"] = f"app;dur={(time.perf_counter() - start) * 1000:.1f}" return responseKey Points
CORSMiddlewarewith explicit origins@app.middleware('http')for simple cases- Pure ASGI middleware for performance
- Order: last added runs first
Deep dive: Request Lifecycle course: FastAPI and Starlette: Middleware, Exceptions and Dependencies
-
Most asked How do you handle errors and customize error responses in FastAPI?
Summary
Raise
HTTPException(status_code, detail)for expected errors, define domain exceptions with@app.exception_handler(MyError)to map them to responses, and overrideRequestValidationErrorhandling to customize 422 payloads.Longer Answer
Keep domain code framework-agnostic by raising your own exceptions (
OrderNotFound) and translating them at the API boundary. A catch-all handler forExceptionshould log with a request ID and return a generic 500 without internals. Consider RFC 9457 Problem Details as a consistent error format.Code Example
class OrderNotFound(Exception): def __init__(self, order_id: int): self.order_id = order_id @app.exception_handler(OrderNotFound) async def order_not_found(request, exc: OrderNotFound): return JSONResponse(status_code=404, content={"title": "Order not found", "order_id": exc.order_id})Key Points
HTTPExceptionfor HTTP-level errors- Exception handlers map domain errors
- Custom
RequestValidationErrorhandler - Never leak stack traces
-
Most asked How do you structure a larger FastAPI project?
Summary
Split routes into
APIRoutermodules per domain (users, orders), included with prefixes and tags, and separate schemas (Pydantic), models (ORM), services/repositories and dependencies into their own modules.Longer Answer
A common layout:
app/main.py(create app, include routers),app/core/config.py(settings),app/db/(engine, session),app/<domain>/router.py,schemas.py,service.py,models.py. Routers can declare shareddependenciesandresponses. Keep business logic out of route functions so it's testable and reusable by CLI/workers.Code Example
# app/users/router.py router = APIRouter(prefix="/users", tags=["users"]) @router.get("/{user_id}", response_model=UserOut) async def get_user(user_id: int, db: DbSession): return await service.get_user(db, user_id) # app/main.py app.include_router(users.router, prefix="/api/v1")Key Points
APIRouter(prefix='/users', tags=['users'])- Domain-oriented packages
- Thin routes → services
- Centralized settings and DB session dependency
-
Most asked How do you test FastAPI applications?
Summary
Use
TestClient(sync, based on httpx) orhttpx.AsyncClientwithASGITransportfor async tests, override dependencies (DB, auth, settings) withapp.dependency_overrides, and use pytest fixtures for setup.Longer Answer
For DB tests, run each test in a transaction that is rolled back, or use a disposable test database (e.g., Testcontainers). Wrap
TestClient(app)in awithblock to trigger lifespan events. Test both happy paths and validation errors (422), and usepytest-asynciooranyiofor async tests.Code Example
from fastapi.testclient import TestClient def fake_user(): return User(id=1, email="[email protected]", role="admin") def test_reports_requires_admin(): app.dependency_overrides[get_current_user] = fake_user with TestClient(app) as client: r = client.get("/reports") assert r.status_code == 200 app.dependency_overrides.clear()Key Points
TestClient(app)in awithblock runs lifespandependency_overridesto swap DB/authhttpx.AsyncClient(transport=ASGITransport(app))for async- Isolated DB per test
-
Most asked How do you deploy FastAPI in production?
Summary
Run Uvicorn with multiple worker processes (
uvicorn --workers Nor Gunicorn withuvicorn.workers.UvicornWorker), behind a reverse proxy or load balancer, usually in containers, with health checks and proper proxy header handling.Longer Answer
Rule of thumb: start with workers ≈ CPU cores and tune with load tests. In Kubernetes, many teams run one worker per container and scale pods instead. Set
--proxy-headersand--forwarded-allow-ipsbehind a proxy sorequest.clientand scheme are right. The FastAPI CLI (fastapi run) wraps Uvicorn for production.Code Example
# Dockerfile (simplified) FROM python:3.13-slim WORKDIR /app COPY pyproject.toml uv.lock ./ RUN pip install uv && uv sync --frozen --no-dev COPY app ./app CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--proxy-headers"]Key Points
- Multiple worker processes for CPU parallelism
- Reverse proxy/LB in front (TLS, buffering)
- One worker per container in Kubernetes is common
- Health/readiness endpoints
-
Most asked How would you improve the throughput of a FastAPI service?
Summary
Profile first, then: use async drivers end to end, avoid blocking calls in
async def, reuse HTTP clients and DB pools, cache hot reads (Redis), offload heavy work to queues, optimize serialization, and scale workers horizontally.Longer Answer
Common hotspots are DB queries (indexes, N+1), per-request client creation (create one
httpx.AsyncClientin lifespan), and large Pydantic payloads (useresponse_modelthoughtfully, ORJSON, or return pre-serialized data). Tune the DB pool against worker count so you don't exhaust Postgres connections (use PgBouncer). Load test with Locust/k6 and watch event loop lag.Key Points
- No blocking calls in async paths
- Shared clients and pools via lifespan
- Cache + queue offloading
- Pool sizing vs DB connection limits
- Load test and measure event loop lag
-
Most asked Explain coroutines, the event loop and tasks in Python asyncio, as they relate to FastAPI.
Summary
A coroutine (
async def) is a pausable function; the event loop runs coroutines, switching between them whenever one awaits I/O; a Task wraps a coroutine so it runs concurrently. FastAPI runs eachasync defrequest handler as a coroutine on Uvicorn's event loop.Longer Answer
Concurrency comes from cooperative yielding at
awaitpoints, so a single thread can serve thousands of waiting requests. If a coroutine never awaits (CPU work) or calls blocking functions, nothing else can run.asyncio.create_task()schedules work concurrently, but tasks started inside a request without tracking can be garbage-collected or outlive the request.Key Points
- Coroutines yield control at
await - Single-threaded cooperative scheduling
- Tasks run coroutines concurrently
- Blocking code stalls the whole loop
- Coroutines yield control at
-
Most asked Can you use synchronous SQLAlchemy with FastAPI? What are the trade-offs?
Summary
Yes: use plain
defendpoints and a sync session dependency, and FastAPI runs them in a threadpool. It's simpler and mature, but concurrency is limited by the threadpool size (about 40 by default via AnyIO) and the DB pool.Longer Answer
Never call a sync session from
async def, because it blocks the loop. Mixing styles is fine per endpoint. You can raise the AnyIO thread limiter if needed. For high-concurrency, I/O-heavy services, async drivers scale better.Key Points
- Sync DB →
defendpoints - Threadpool capacity limits concurrency
- Never use sync sessions in
async def
- Sync DB →
- What changed between Pydantic v1 and v2 that affects FastAPI code?
- How do you manage configuration and secrets in FastAPI?
- How does FastAPI generate OpenAPI docs, and how can you customize them?
- How do you return different status codes and response types?
- How do you handle field aliases (e.g., camelCase JSON with snake_case Python) in Pydantic v2?
- What are discriminated unions in Pydantic and why are they useful in APIs?
- How do you create reusable validated types with `Annotated` in Pydantic v2?
- How do class-based dependencies and dependencies with parameters work?
- How do OAuth2 scopes work with `Security()` in FastAPI?
- How would you implement API key authentication in FastAPI?
- How would you add rate limiting to a FastAPI service?
- How do you cache responses or data in FastAPI?
- How do you integrate Celery (or another task queue) with FastAPI?
- How do you add observability (logs, metrics, traces) to FastAPI?
- How do you run several I/O calls concurrently inside a FastAPI endpoint?
- How do you call external APIs reliably from FastAPI?
- How do you make POST endpoints idempotent (e.g., payments)?
- How do you securely receive webhooks in FastAPI?
- How do you manage database migrations in a FastAPI + SQLAlchemy project?
- How do you implement refresh tokens and logout with JWT access tokens?
- Why does raising HTTPException inside a FastAPI middleware produce a 500 instead of the intended status?
- How do you stream large responses or handle file uploads in FastAPI?
- How do WebSockets work in FastAPI?
- How would you implement pagination in a FastAPI endpoint?
- How would you version a FastAPI API?
- What does Starlette provide underneath FastAPI?
- How do computed fields and custom serializers work in Pydantic v2?
- How do you reject unknown fields or disable type coercion in request models?
- How do you build generic response envelopes with Pydantic?
- How do you apply a dependency to all routes in a router or the whole app?
- If a FastAPI backend uses cookie-based auth for a browser SPA, what must you add?
- How should you implement health checks for a FastAPI service?
- How do you share per-request data (like request ID or tenant) across layers?
- How do you accept form data in FastAPI?
- How would you implement ETags and conditional requests in FastAPI?
- What is SQLModel and when would you choose it?
- How would you use MongoDB with FastAPI?
- Can you serve GraphQL with FastAPI?
- How would you serve a machine learning model with FastAPI?
- How do you write async tests for FastAPI with httpx?
- How do you mock external HTTP calls in FastAPI tests?
- What is Schemathesis and how does it help test FastAPI apps?
- How do you type-check a FastAPI codebase?
- How does FastAPI compare with Litestar and Django Ninja?
- How would you implement multi-tenancy in a FastAPI SaaS?
- How do you prevent lost updates when two clients edit the same record?
- Which security settings should a production FastAPI app have?
- What are uvloop and httptools, and how do they relate to Uvicorn?
- What does dependency caching mean in FastAPI and when would you disable it?
- How do you set cookies and headers on a FastAPI response?
- Can FastAPI render HTML and serve static files?
- What does FastAPI's default 422 validation error look like, and how can you customize it?
- How do you handle client disconnects or cancellations in long-running FastAPI requests?
- How should a FastAPI API handle datetimes and money values?
- How do you restrict a parameter to a fixed set of values?
- Why use an application factory (`create_app()`) in FastAPI?
- How would you add 'Login with Google' (OIDC) to a FastAPI app?
- How should passwords be hashed in a FastAPI app?
- How would you design role-based access control in FastAPI?
- How would you implement audit logging for sensitive actions?
- What are the trade-offs of soft deletes in a FastAPI/SQLAlchemy API?
- Can FastAPI run on AWS Lambda or other serverless platforms?
- How do you achieve graceful shutdown and zero-downtime deploys with FastAPI?
- How do you enable response compression in FastAPI?
- How can you speed up JSON serialization in FastAPI?
- What is the difference between `include_router` and `app.mount`?
- When would you write a custom `APIRoute` class?
- How do frontend teams consume a FastAPI API safely?
- What role does AnyIO play in FastAPI?
- How would you stream LLM responses to a browser with FastAPI?
- What does a good CI pipeline for a FastAPI service include?
- What is the transactional outbox pattern and why use it with FastAPI?
- How do you read and validate headers and cookies in FastAPI?
- How would you apply clean/hexagonal architecture in a FastAPI codebase?
- What Gunicorn/Uvicorn settings matter in production?
- How do you scale a FastAPI service on Kubernetes?
- How would you return localized error messages from a FastAPI API?
- Should a FastAPI API expose auto-increment IDs or UUIDs?