Tutorials Logic, IN info@tutorialslogic.com
FastAPI

Top 50 FastAPI Interview Questions

FastAPI interview questions covering async APIs, Pydantic, dependency injection, validation, auth, testing, OpenAPI, and deployment.

01

What is FastAPI?

FastAPI is a modern Python web framework for building APIs with type hints. It is built on Starlette for the web layer and Pydantic for data validation and serialization. It is popular because it provides high performance, automatic OpenAPI documentation, dependency injection, async support, and clean request/response validation.

02

Why is FastAPI considered fast?

FastAPI is fast because it runs on ASGI, uses Starlette for efficient async request handling, and avoids unnecessary framework overhead. Performance also depends on the application code: blocking database calls inside async endpoints, slow queries, large payloads, and CPU-heavy work can still make a FastAPI app slow.

03

How do you create a basic FastAPI application?

Create a FastAPI instance and define path operation functions with decorators such as @app.get() or @app.post(). Uvicorn is commonly used to run the ASGI app during development and production.

Example
from fastapi import FastAPI

app = FastAPI()

@app.get('/health')
def health():
    return {'status': 'ok'}
04

What is a path operation in FastAPI?

A path operation is the combination of a URL path, an HTTP method, and the Python function that handles it. For example, @app.get("/users/{user_id}") defines a GET operation for a dynamic user route. Path operations are the core building blocks of FastAPI APIs.

05

How do path parameters work in FastAPI?

Path parameters are dynamic values inside the URL path. FastAPI reads them from the route and validates them using Python type hints. If a path parameter is declared as int and the client sends a non-integer value, FastAPI automatically returns a validation error.

Example
from fastapi import FastAPI

app = FastAPI()

@app.get('/users/{user_id}')
def get_user(user_id: int):
    return {'user_id': user_id}
06

How do query parameters work in FastAPI?

Query parameters are values after the question mark in a URL, such as ?page=1&limit=20. FastAPI maps function parameters that are not path parameters to query parameters by default. They are useful for filtering, sorting, searching, and pagination.

Example
@app.get('/products')
def list_products(page: int = 1, limit: int = 20, search: str | None = None):
    return {'page': page, 'limit': limit, 'search': search}
07

What is Pydantic used for in FastAPI?

Pydantic validates input data, converts data types, defines request and response schemas, and powers OpenAPI schema generation. In FastAPI, Pydantic models are commonly used for request bodies, response models, settings, and nested validation rules.

08

How do you define a request body with Pydantic?

Define a class that inherits from BaseModel and use it as a function parameter. FastAPI treats it as a JSON request body, validates it, and passes a typed object into the handler.

Example
from pydantic import BaseModel, EmailStr

class CreateUser(BaseModel):
    name: str
    email: EmailStr

@app.post('/users')
def create_user(payload: CreateUser):
    return payload
09

What is a response_model in FastAPI?

response_model defines the shape of the response returned to the client. It validates and serializes output data and can prevent leaking internal fields. For example, a database user object may include password_hash, but the response model should exclude it.

Example
class UserOut(BaseModel):
    id: int
    name: str
    email: EmailStr

@app.get('/users/{user_id}', response_model=UserOut)
def get_user(user_id: int):
    return {'id': user_id, 'name': 'Asha', 'email': 'asha@example.com', 'password_hash': 'hidden'}
10

How does FastAPI validation work?

FastAPI validates path parameters, query parameters, headers, cookies, and request bodies based on type hints and Pydantic models. Invalid input returns a 422 response with structured error details. This lets APIs reject bad data before business logic runs.

11

How do you add constraints to query parameters?

Use Query, Path, Body, Header, or Cookie helpers to add constraints and metadata. These constraints appear in OpenAPI docs and are enforced at runtime.

Example
from fastapi import Query

@app.get('/items')
def list_items(limit: int = Query(20, ge=1, le=100)):
    return {'limit': limit}
12

What is dependency injection in FastAPI?

FastAPI dependency injection lets route handlers declare reusable dependencies with Depends. Dependencies can provide database sessions, authenticated users, settings, clients, permissions, pagination values, and shared validation. FastAPI resolves them per request and can cache dependency results within the same request.

Example
from fastapi import Depends

def get_settings():
    return {'debug': False}

@app.get('/config')
def read_config(settings = Depends(get_settings)):
    return settings
13

How do dependency overrides help in testing?

Dependency overrides let tests replace real dependencies with fake ones. This is useful for swapping real databases, external APIs, authenticated users, or configuration. It makes route tests faster and more deterministic.

Example
def fake_current_user():
    return {'id': 1, 'role': 'admin'}

app.dependency_overrides[get_current_user] = fake_current_user
14

What is the difference between sync and async endpoints?

A sync endpoint uses def and is suitable for ordinary blocking code. An async endpoint uses async def and can await non-blocking I/O such as async database drivers or HTTP clients. Do not put blocking operations directly inside async endpoints because they can block the event loop and reduce concurrency.

15

When should you use async def in FastAPI?

Use async def when the handler awaits asynchronous libraries, such as async SQLAlchemy, asyncpg, httpx.AsyncClient, or Redis async clients. If your code uses blocking libraries, a normal def endpoint may be safer unless you move blocking work to a thread pool or use async-compatible libraries.

Example
import httpx

@app.get('/profile/{user_id}')
async def profile(user_id: int):
    async with httpx.AsyncClient() as client:
        response = await client.get(f'https://example.com/users/{user_id}')
    return response.json()
16

What is ASGI, and why does FastAPI use it?

ASGI, or Asynchronous Server Gateway Interface, is the Python standard interface for async web servers and applications. It supports HTTP, WebSockets, and long-lived connections. FastAPI uses ASGI through Starlette, which enables async request handling and WebSocket support.

17

What is Uvicorn in a FastAPI project?

Uvicorn is an ASGI server used to run FastAPI applications. It receives HTTP requests, manages the event loop, and calls the FastAPI app. In production, Uvicorn may run directly or behind Gunicorn, a process manager, a container orchestrator, or a reverse proxy.

Example
uvicorn main:app --host 0.0.0.0 --port 8000
18

What is APIRouter in FastAPI?

APIRouter groups related routes into reusable modules. It helps organize large projects by feature, such as users, orders, auth, and admin. Routers can define prefixes, tags, dependencies, and response behavior.

Example
from fastapi import APIRouter

router = APIRouter(prefix='/users', tags=['users'])

@router.get('/')
def list_users():
    return []

app.include_router(router)
19

How should a FastAPI project be structured?

A common structure separates routers, schemas, services, database models, dependencies, configuration, and tests. The exact folder names matter less than keeping HTTP routing separate from business logic and persistence details.

Example
app/
  main.py
  routers/
  schemas/
  services/
  models/
  dependencies.py
  database.py
  config.py
  tests/
20

How do you handle errors in FastAPI?

Use HTTPException for expected client-facing errors and exception handlers for consistent error formatting. Unexpected exceptions should be logged and converted into safe 500 responses without leaking stack traces or secrets to clients.

Example
from fastapi import HTTPException

@app.get('/users/{user_id}')
def get_user(user_id: int):
    user = find_user(user_id)
    if not user:
        raise HTTPException(status_code=404, detail='User not found')
    return user
21

How do custom exception handlers work in FastAPI?

Custom exception handlers let you control the response for specific exception types. They are useful for consistent API error shapes, mapping domain exceptions to HTTP status codes, and including request IDs in error responses.

Example
from fastapi import Request
from fastapi.responses import JSONResponse

class DomainError(Exception):
    pass

@app.exception_handler(DomainError)
async def domain_error_handler(request: Request, exc: DomainError):
    return JSONResponse(status_code=400, content={'error': str(exc)})
22

How do you add middleware in FastAPI?

Middleware runs around every request and response. It is commonly used for request IDs, logging, timing, security headers, CORS, and tracing. Keep middleware lightweight because it affects every request.

Example
import time

@app.middleware('http')
async def add_process_time(request, call_next):
    started = time.time()
    response = await call_next(request)
    response.headers['X-Process-Time'] = str(time.time() - started)
    return response
23

How do you configure CORS in FastAPI?

Use CORSMiddleware to control which browser origins can call the API. Avoid wildcard origins for authenticated APIs. Configure allowed origins, methods, headers, and credentials based on real frontend environments.

Example
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=['https://example.com'],
    allow_credentials=True,
    allow_methods=['GET', 'POST'],
    allow_headers=['Authorization', 'Content-Type'],
)
24

How do you implement JWT authentication in FastAPI?

JWT authentication usually reads a bearer token, verifies its signature and claims, and returns the current user as a dependency. Production systems should handle expiration, refresh tokens, revocation, secret rotation, and secure token storage on the client.

Example
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer

security = OAuth2PasswordBearer(tokenUrl='token')

def get_current_user(token: str = Depends(security)):
    payload = verify_jwt(token)
    if not payload:
        raise HTTPException(status_code=401, detail='Invalid token')
    return payload
25

What is OAuth2PasswordBearer in FastAPI?

OAuth2PasswordBearer is a security dependency that extracts bearer tokens from the Authorization header and documents the auth scheme in OpenAPI. It does not verify the token by itself; your dependency must validate the token and load the user.

26

How do you implement role-based authorization?

Use dependencies that first authenticate the user and then check roles or permissions. For complex systems, move permission logic into a policy layer rather than scattering role checks across every route.

Example
def require_admin(user = Depends(get_current_user)):
    if user.get('role') != 'admin':
        raise HTTPException(status_code=403, detail='Forbidden')
    return user

@app.delete('/users/{user_id}')
def delete_user(user_id: int, admin = Depends(require_admin)):
    return {'deleted': user_id}
27

How do background tasks work in FastAPI?

BackgroundTasks runs simple work after the response is sent, such as sending an email or writing an audit record. It is not a durable job queue. For important or long-running jobs, use Celery, RQ, Dramatiq, Arq, or a cloud queue.

Example
from fastapi import BackgroundTasks

def send_email(email: str):
    print('Sending email to', email)

@app.post('/signup')
def signup(email: str, tasks: BackgroundTasks):
    tasks.add_task(send_email, email)
    return {'status': 'created'}
28

How do lifespan events work in FastAPI?

Lifespan events run startup and shutdown logic, such as opening database pools, warming caches, loading models, or closing clients. The modern pattern uses an async context manager passed to FastAPI(lifespan=...).

Example
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.cache = await create_cache()
    yield
    await app.state.cache.close()

app = FastAPI(lifespan=lifespan)
29

How do you manage database sessions in FastAPI?

A common pattern is to provide a database session through a dependency that yields the session and closes it after the request. This prevents leaked connections and keeps transaction handling predictable.

Example
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get('/users')
def list_users(db: Session = Depends(get_db)):
    return db.query(User).all()
30

How do you use SQLAlchemy with FastAPI?

Use SQLAlchemy for models, sessions, queries, and transactions. In sync apps, use normal SQLAlchemy sessions with def routes or dependencies. In async apps, use SQLAlchemy async engine and AsyncSession. Avoid mixing blocking sync database calls directly inside async endpoints.

31

What is Alembic used for in FastAPI projects?

Alembic manages database migrations. It records schema changes as migration files and applies them consistently across environments. In interviews, mention that migrations should be reviewed, tested on staging data, and included in deployment planning.

32

How do you handle file uploads in FastAPI?

Use UploadFile and File for multipart uploads. UploadFile streams through a file-like object and is better for larger files than reading the entire body into memory. Validate size, content type, and storage destination.

Example
from fastapi import File, UploadFile

@app.post('/upload')
async def upload(file: UploadFile = File(...)):
    content = await file.read()
    return {'filename': file.filename, 'size': len(content)}
33

How do form fields work in FastAPI?

Use Form for application/x-www-form-urlencoded or multipart form fields. OAuth2 password login commonly uses form data because the OAuth2 password flow expects username and password form fields.

Example
from fastapi import Form

@app.post('/login')
def login(username: str = Form(...), password: str = Form(...)):
    return {'username': username}
34

How do WebSockets work in FastAPI?

FastAPI supports WebSockets through Starlette. A WebSocket endpoint accepts the connection, receives messages, sends responses, and closes the connection when needed. Production systems must handle authentication, connection limits, broadcasting, and horizontal scaling.

Example
from fastapi import WebSocket

@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    message = await websocket.receive_text()
    await websocket.send_text('Echo: ' + message)
35

What are streaming responses in FastAPI?

StreamingResponse sends data gradually instead of building the whole response in memory. It is useful for large files, logs, server-generated output, and long-running streams. Be careful with timeouts, client disconnects, and blocking generators.

Example
from fastapi.responses import StreamingResponse

async def stream_numbers():
    for number in range(3):
        yield f'{number}\n'

@app.get('/stream')
def stream():
    return StreamingResponse(stream_numbers(), media_type='text/plain')
36

How does FastAPI generate OpenAPI documentation?

FastAPI uses route definitions, type hints, Pydantic models, status codes, tags, summaries, descriptions, and response models to generate an OpenAPI schema. Swagger UI and ReDoc are served automatically unless disabled or customized.

37

How do you customize FastAPI documentation?

You can set title, description, version, tags, summaries, response descriptions, examples, and docs URLs. You can also disable docs in production or protect them behind authentication if the schema exposes internal API details.

Example
app = FastAPI(
    title='Orders API',
    version='1.0.0',
    docs_url='/docs',
    redoc_url='/redoc',
)
38

How do you set response status codes?

Set status_code in the route decorator for normal responses, or raise HTTPException for error responses. Use 201 for created resources, 204 for no-content responses, 400 for invalid client input, 401 for unauthenticated requests, 403 for forbidden requests, and 404 for missing resources.

Example
from fastapi import status

@app.post('/users', status_code=status.HTTP_201_CREATED)
def create_user(payload: CreateUser):
    return {'id': 1, **payload.model_dump()}
39

How do you test FastAPI endpoints?

Use TestClient for sync-style tests or httpx.AsyncClient for async tests. Tests should cover success paths, validation errors, authentication, authorization, dependency overrides, and error responses.

Example
from fastapi.testclient import TestClient

client = TestClient(app)

def test_health():
    response = client.get('/health')
    assert response.status_code == 200
    assert response.json() == {'status': 'ok'}
40

How do you handle settings in FastAPI?

Use pydantic-settings or Pydantic BaseSettings to load environment variables into a typed settings object. Cache the settings dependency so it is not rebuilt repeatedly. Validate required configuration at startup.

Example
from functools import lru_cache
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    jwt_secret: str

@lru_cache
def get_settings():
    return Settings()
41

How do you implement pagination in FastAPI?

Accept page and limit or cursor parameters, validate bounds, and return pagination metadata. Offset pagination is simple, while cursor pagination works better for large or frequently changing datasets.

Example
@app.get('/products')
def products(page: int = Query(1, ge=1), limit: int = Query(20, ge=1, le=100)):
    offset = (page - 1) * limit
    return {'page': page, 'limit': limit, 'offset': offset, 'data': []}
42

How do you add rate limiting to FastAPI?

FastAPI does not include rate limiting by default. Use middleware or libraries backed by Redis for distributed deployments. Apply stricter limits to login, OTP, password reset, and public search endpoints. In-memory rate limits are not enough when multiple app instances run.

43

How do you add request logging and request IDs?

Use middleware to generate or read a request ID, attach it to response headers, and include it in logs. This helps trace one request across application logs, reverse proxies, and downstream services.

Example
from uuid import uuid4

@app.middleware('http')
async def request_id_middleware(request, call_next):
    request_id = request.headers.get('x-request-id', str(uuid4()))
    response = await call_next(request)
    response.headers['x-request-id'] = request_id
    return response
44

How do you deploy FastAPI with Docker?

Package the app with dependencies, run it with Uvicorn or Gunicorn plus Uvicorn workers, expose the port, and provide configuration through environment variables. Use a slim image, avoid baking secrets into the image, and add health checks in the container platform.

Example
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
45

How should Gunicorn workers be configured for FastAPI?

Gunicorn can manage multiple Uvicorn worker processes. Worker count depends on CPU, memory, workload, blocking behavior, and latency goals. Too few workers reduce concurrency; too many can waste memory or overload the database. Always test with realistic traffic.

Example
gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000
46

How do you implement health checks in FastAPI?

Health checks help load balancers and orchestrators know whether the app is alive and ready. A liveness endpoint can simply confirm the process responds, while a readiness endpoint can verify dependencies such as the database, cache, or queue. Keep health checks fast because they may be called frequently.

Example
@app.get('/health/live')
def live():
    return {'status': 'alive'}

@app.get('/health/ready')
def ready():
    return {'database': 'ok'}
47

How do you improve FastAPI performance?

Avoid blocking calls inside async endpoints, optimize database queries, use connection pools, limit payload size, stream large responses, cache safe data, tune workers, add timeouts, and monitor p95/p99 latency. FastAPI is efficient, but the application architecture and dependencies usually decide real performance.

48

What are common FastAPI security mistakes?

Common mistakes include allowing broad CORS with credentials, skipping input validation for nested data, leaking stack traces, trusting JWTs without verifying signatures, using weak secrets, missing rate limits on auth endpoints, storing passwords without strong hashing, and exposing internal OpenAPI docs publicly.

49

What are common FastAPI anti-patterns?

Common anti-patterns include putting all logic in route functions, mixing database queries with response formatting everywhere, using blocking I/O in async routes, returning ORM objects without response models, ignoring dependency overrides in tests, and using BackgroundTasks for critical durable jobs.

50

How would you build a simple CRUD endpoint in FastAPI?

A good CRUD example uses Pydantic schemas, clear routes, validation, response models, and service or database layers outside the route when the app grows. This small in-memory example shows the basic API shape.

Example
class UserCreate(BaseModel):
    name: str
    email: EmailStr

class UserOut(UserCreate):
    id: int

users: dict[int, UserOut] = {}

@app.post('/users', response_model=UserOut, status_code=201)
def create_user(payload: UserCreate):
    user = UserOut(id=len(users) + 1, **payload.model_dump())
    users[user.id] = user
    return user

@app.get('/users/{user_id}', response_model=UserOut)
def read_user(user_id: int):
    if user_id not in users:
        raise HTTPException(status_code=404, detail='User not found')
    return users[user_id]

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.