Top 44 FastAPI / Flask Interview Questions & Answers (2026)
About FastAPI / Flask
Top 50 FastAPI and Flask interview questions covering routing, async, Pydantic, SQLAlchemy, authentication, deployment, and Python web API best practices. Companies hiring for FastAPI / Flask roles test this knowledge at every stage — from first-round screens that check the basics to final-round conversations about real-world trade-offs.
What to Expect in a FastAPI / Flask Interview
Expect a mix of conceptual and practical FastAPI / Flask questions: clear definitions and core-concept checks for junior roles, hands-on scenario questions for mid-level roles, and architecture or trade-off discussions for senior roles. Interviewers usually move from foundational topics toward the kind of problems you'd actually face on the job.
How to Use This Guide
Work through the FastAPI / Flask questions in order — Beginner, then Intermediate, then Advanced — so each concept builds on the last. Every question has its own page; bookmark the ones that trip you up and revisit them the day before your interview.
Curated by Tech Baithak Editorial Team · Last updated: June 2026
Core concepts every FastAPI / Flask developer must know.
01
What is Flask?
Flask is a lightweight, micro web framework for Python created by Armin Ronacher. It is called "micro" because it keeps the core simple and extensible — no built-in ORM, form validation, or authentication. Flask is based on the Werkzeug WSGI toolkit and Jinja2 template engine. Its philosophy is to keep simple things simple and allow developers to choose their own libraries. Flask is ideal for small to medium APIs, microservices, and projects where you want full control over your stack. It has a large ecosystem of extensions (Flask-SQLAlchemy, Flask-Login, Flask-JWT-Extended) that add functionality on demand. Flask uses a synchronous request-response model by default (WSGI), though Flask 2.0+ supports async views.
02
What is FastAPI?
FastAPI is a modern, high-performance Python web framework for building APIs, created by Sebastián Ramírez. It is built on Starlette (for async HTTP) and Pydantic (for data validation). Key features: Async first: built on Python's async/await and the ASGI standard for non-blocking I/O. Automatic API documentation: generates interactive Swagger UI and ReDoc documentation from your code. Type hints: uses Python type annotations for automatic validation, serialization, and documentation. Performance: comparable to NodeJS and Go — one of the fastest Python frameworks. Pydantic models: define request/response schemas with automatic validation. FastAPI is ideal for high-performance APIs, microservices, and ML model serving where type safety and auto-documentation are valued.
03
What is the difference between WSGI and ASGI?
WSGI (Web Server Gateway Interface) is a synchronous Python standard for web servers and web applications to communicate. One request is handled at a time per process/thread — the server calls the WSGI application, waits for the response, then handles the next request. Flask and Django (by default) are WSGI frameworks. ASGI (Asynchronous Server Gateway Interface) is the modern async successor. It supports async handlers (async def) and long-lived connections like WebSockets and Server-Sent Events. Multiple requests can be handled concurrently in a single thread using Python's event loop. FastAPI and Django Channels are ASGI frameworks. Servers: WSGI — Gunicorn, uWSGI. ASGI — Uvicorn, Hypercorn, Daphne. For I/O-bound workloads (database queries, HTTP calls), ASGI with async handlers provides dramatically better concurrency than synchronous WSGI.
04
How do you create a basic Flask application?
Create a Flask app in minimal code: from flask import Flask; app = Flask(__name__); @app.route('/') def home(): return 'Hello World'; if __name__ == '__main__': app.run(debug=True). The Flask(__name__) creates the app instance — __name__ tells Flask where to look for templates and static files. @app.route('/') registers the function as the handler for GET /. app.run(debug=True) starts the development server with auto-reload. For production, use Gunicorn: gunicorn app:app -w 4. The Flask application object is the central point — you register routes, configure extensions, and create the WSGI callable through it. __name__ is important: it helps Flask find your package resources relative to the file.
05
How do you create a basic FastAPI application?
Create a FastAPI app: from fastapi import FastAPI; app = FastAPI(); @app.get('/') async def root(): return {'message': 'Hello World'}. Run with Uvicorn: uvicorn main:app --reload. FastAPI automatically generates Swagger UI at /docs and ReDoc at /redoc. Use type hints for automatic validation: @app.get('/items/{item_id}') async def get_item(item_id: int, q: str = None): return {'id': item_id, 'q': q} — FastAPI validates that item_id is an integer, returns a 422 error if it isn't. Query parameters are declared as function arguments with defaults. Response models are Pydantic classes. The combination of type hints + Pydantic + automatic documentation makes FastAPI one of the most productive Python frameworks for API development.
06
What is Pydantic and how does it work in FastAPI?
Pydantic is a Python data validation library that uses type annotations to validate and serialize data. In FastAPI, Pydantic models define request and response schemas. Define a model: from pydantic import BaseModel; class User(BaseModel): name: str; age: int; email: str. Use as request body: @app.post('/users') async def create(user: User): .... FastAPI automatically validates the request JSON against the User model, returning a 422 Unprocessable Entity with detailed errors if validation fails. Pydantic supports: default values, optional fields (Optional[str]), validators (@validator), custom types, and nested models. It also handles serialization: converting ORM objects to JSON-serializable dicts via orm_mode = True (Pydantic v1) or from_attributes = True (Pydantic v2).
07
What are Flask Blueprints?
Flask Blueprints are a way to organize a Flask application into modular, reusable components. A blueprint is like a mini-application that records operations to be registered on the main app. Create: from flask import Blueprint; auth = Blueprint('auth', __name__, url_prefix='/auth'). Define routes on the blueprint: @auth.route('/login') def login(): .... Register in the main app: app.register_blueprint(auth). Blueprints enable separating a large application into feature modules (auth, users, products) each in their own directory with their own routes, templates, and static files. All routes in a blueprint get the prefix and name: url_for('auth.login'). Blueprints make large Flask apps as organized as frameworks like Django without sacrificing Flask's flexibility.
08
What is FastAPI's dependency injection system?
FastAPI has a built-in dependency injection system using the Depends() function. Dependencies are callable functions that are called automatically and their return values are injected into route handlers. Example: def get_db(): db = Session(); try: yield db; finally: db.close(). Use in a route: @app.get('/users') async def get_users(db: Session = Depends(get_db)): return db.query(User).all(). FastAPI calls get_db() before the handler, injects the session, and runs the cleanup after. Dependencies can depend on other dependencies (chaining). Common uses: database sessions, authentication (extract and verify JWT, inject current user), config injection, and shared request parsing. Dependencies can also be classes with a __call__ method. This system makes route handlers clean and testable.
09
How do you handle path parameters in Flask and FastAPI?
Flask: path parameters use angle brackets: @app.route('/users/<int:user_id>'). The function receives the parameter as a keyword argument: def get_user(user_id): .... Type converters: int, float, string, path, uuid. Flask validates the type automatically. FastAPI: path parameters use curly braces: @app.get('/users/{user_id}'). Declare the parameter in the function signature with a type hint: async def get_user(user_id: int): .... FastAPI uses the type hint for validation and converts the string to the declared type. You can also add validation with Path(): user_id: int = Path(..., gt=0, le=1000). Both frameworks return appropriate error responses for type mismatches.
10
What is Flask's application context and request context?
Flask has two contexts that make certain objects globally accessible during request handling. Application Context: pushed when the app starts handling a request or when explicitly entered. Provides access to g (request-scoped storage for sharing data between functions in one request) and current_app (proxy to the Flask app). Request Context: pushed for each incoming request. Provides request (the current HTTP request object with request.args, request.json, request.headers) and session (cookie-based session). Flask uses thread-local proxies to make these available globally without passing them around. In testing, manually push contexts: with app.app_context():. Understanding contexts is crucial for avoiding the common RuntimeError: Working outside of application context error.
11
What is SQLAlchemy and how is it used in Flask?
SQLAlchemy is Python's most popular ORM (Object-Relational Mapper). Flask-SQLAlchemy integrates it with Flask, providing a convenient setup. Install: pip install flask-sqlalchemy. Configure: app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'; db = SQLAlchemy(app). Define a model: class User(db.Model): id = db.Column(db.Integer, primary_key=True); name = db.Column(db.String(100), nullable=False). Create tables: db.create_all(). Query: User.query.all(), User.query.filter_by(name='Alice').first(). Insert: db.session.add(user); db.session.commit(). Flask-SQLAlchemy automatically handles connection pooling, session lifecycle tied to the request context, and supports multiple databases.
12
How does FastAPI handle request validation and error responses?
FastAPI uses Pydantic for automatic request validation at every layer. When validation fails, FastAPI returns a 422 Unprocessable Entity response with a detailed JSON body listing every validation error — field name, location (body/path/query), error type, and message. This is automatic — no manual validation code needed. Example error: {"detail": [{"loc": ["body", "age"], "msg": "value is not a valid integer", "type": "type_error.integer"}]}. For custom HTTP errors, raise HTTPException: from fastapi import HTTPException; raise HTTPException(status_code=404, detail='User not found'). Create custom exception handlers: @app.exception_handler(CustomError) async def handler(request, exc): return JSONResponse(status_code=400, content={'error': str(exc)}). FastAPI's validation layer is one of its biggest productivity advantages.
13
What is Flask's g object?
The g object in Flask is a global namespace object available during the lifetime of a request for storing data that multiple functions in the same request might need. It is created fresh for every new request and discarded at the end. Common uses: storing the current authenticated user after JWT verification in a before_request function, caching a database connection for the request, or sharing a traced request ID. Access it: from flask import g; g.current_user = user. Read it later: user = g.get('current_user', None). The g object is thread/context safe — each request has its own g even in multi-threaded servers. Do not confuse it with the Flask application config (app.config) which persists across requests, or the session which persists across user browser sessions.
14
What is CORS and how do you enable it in Flask and FastAPI?
CORS (Cross-Origin Resource Sharing) allows web browsers to make API requests to a different domain. Flask: use flask-cors: from flask_cors import CORS; CORS(app) allows all origins, or CORS(app, origins=['https://myapp.com']) for specific origins. You can also apply per-route: @cross_origin(). FastAPI: use the built-in CORSMiddleware: from fastapi.middleware.cors import CORSMiddleware; app.add_middleware(CORSMiddleware, allow_origins=['https://myapp.com'], allow_methods=['*'], allow_headers=['*'], allow_credentials=True). In development, allow all origins for convenience. In production, always specify exact allowed origins. CORS does not affect server-to-server calls or tools like Postman — it only affects browser requests to cross-origin APIs.
15
What is Flask-Migrate?
Flask-Migrate is a Flask extension that handles SQLAlchemy database schema migrations using Alembic. Without migrations, db.create_all() only creates new tables but does not modify existing ones — you cannot add columns or change types without dropping and recreating tables. Flask-Migrate tracks schema changes as migration scripts. Commands: flask db init (initialize migration folder). flask db migrate -m "Add phone column" (auto-generate migration from model changes). flask db upgrade (apply pending migrations). flask db downgrade (roll back last migration). Migration scripts are Python files stored in the migrations/ folder and should be committed to version control. Always review auto-generated migrations — Alembic may not detect all changes (e.g., column renames) and may generate incorrect migration SQL.
16
What are FastAPI response models?
Response models in FastAPI define the schema of the response data. Declare with the response_model parameter on the route decorator: @app.get('/users/{id}', response_model=UserResponse). FastAPI validates the returned data against the Pydantic model and serializes it, automatically filtering out fields not in the response model — even if the underlying ORM object has sensitive fields like password_hash. Create separate request and response models: class UserCreate(BaseModel): name: str; password: str and class UserResponse(BaseModel): id: int; name: str. FastAPI also uses the response model for auto-documentation, showing the exact response schema in Swagger UI. Set response_model_exclude_unset=True to omit fields that were not set (useful for PATCH endpoints).
17
What is Flask's before_request and after_request?
@app.before_request registers a function that runs before every request — regardless of which route handles it. Common uses: authenticate the request (verify JWT, set g.current_user), open database connections, check maintenance mode. If the function returns a response, Flask uses that response and skips the route handler. @app.after_request registers a function that runs after every request, receiving the response object. It must return the response (modified or as-is). Common uses: add CORS headers, log request duration, modify response headers. @app.teardown_request runs after the response is sent — used for cleanup regardless of exceptions (close DB connections). Blueprint-level versions (@blueprint.before_request) only apply to that blueprint's routes.
18
How do you handle environment configuration in Flask?
Flask configuration is managed through the app.config dictionary. Best practices: Class-based config: create a config.py with classes for each environment: class DevelopmentConfig: DEBUG = True; SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'. Load with app.config.from_object(DevelopmentConfig). Environment variables: use python-dotenv to load a .env file: from dotenv import load_dotenv; load_dotenv(). Read variables: os.environ.get('SECRET_KEY'). Instance folder: store sensitive local config in instance/config.py (not committed to git). Load with app.config.from_pyfile('config.py'). The FLASK_ENV and FLASK_DEBUG environment variables control development/production mode. Never hardcode secrets in source code.
19
What is FastAPI's async support?
FastAPI is built on ASGI and fully supports Python's async/await for non-blocking I/O. Define async route handlers with async def: @app.get('/data') async def get_data(): result = await fetch_from_db(); return result. When you use async def, FastAPI runs the handler in the event loop directly — it can handle thousands of concurrent requests without threads. For CPU-bound work or code that cannot be made async (calling synchronous libraries), use def instead of async def — FastAPI runs sync handlers in a thread pool automatically, preventing the event loop from blocking. This is a key insight: sync handlers are fine in FastAPI but won't benefit from the async concurrency. Use async def when calling async-compatible libraries: httpx (not requests), asyncpg (not psycopg2), motor (async MongoDB).
20
What is Flask's session object?
Flask's session is a dictionary-like object that persists data across requests for a specific user via a signed cookie. Values stored in the session are serialized to JSON, signed with the app's SECRET_KEY (preventing tampering), and stored in the user's browser cookie. Usage: from flask import session; session['user_id'] = user.id. Read: user_id = session.get('user_id'). Clear: session.clear(). Important: session data is stored client-side (in the cookie) — it is signed but not encrypted. Do not store sensitive data (passwords, tokens) in the session. For server-side sessions (stored in Redis or database), use Flask-Session. The default cookie size limit is ~4KB — do not store large objects. Set SESSION_COOKIE_SECURE=True for HTTPS-only cookies in production.
21
What is Uvicorn and how is it used with FastAPI?
Uvicorn is a lightning-fast ASGI server for Python, based on uvloop and httptools. It is the standard server for running FastAPI in production. Development: uvicorn main:app --reload --port 8000 — --reload restarts on code changes. Production: uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4. For multi-process production deployments, use Gunicorn with Uvicorn workers: gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker. Gunicorn manages multiple worker processes for resilience; each worker runs Uvicorn's ASGI server. Alternatively, use uvicorn --workers 4 (Uvicorn's built-in multi-process mode). With async FastAPI, a single worker handles many concurrent requests, so you need fewer workers than with synchronous WSGI (typically 1-2 per CPU core instead of 4-8).
Practical knowledge for developers with hands-on experience.
01
How do you implement JWT authentication in FastAPI?
FastAPI JWT authentication uses the dependency injection system. 1. Login endpoint: verify credentials, generate token: access_token = jwt.encode({"sub": user.id, "exp": datetime.utcnow() + timedelta(hours=1)}, SECRET_KEY, algorithm="HS256"). 2. Security scheme: oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token"). 3. Get current user dependency: async def get_current_user(token: str = Depends(oauth2_scheme)): payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]); return await get_user(payload["sub"]). 4. Protected route: @app.get("/me") async def me(user: User = Depends(get_current_user)): return user. FastAPI integrates with the OAuth2PasswordBearer scheme to automatically display the Authorize button in Swagger UI, making API testing easy. Use the python-jose or PyJWT library for token encoding/decoding.
02
How does SQLAlchemy async work with FastAPI?
FastAPI's async support is best paired with SQLAlchemy 1.4+ async engine and asyncpg (PostgreSQL async driver). Setup: from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine; engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db"); async_session = sessionmaker(engine, class_=AsyncSession). Create a dependency: async def get_db(): async with async_session() as session: yield session. Use in routes: result = await db.execute(select(User).where(User.id == id)); user = result.scalar_one_or_none(). The async SQLAlchemy style uses explicit SQL expressions rather than the ORM query API. Key difference from sync SQLAlchemy: db.query(User) becomes await db.execute(select(User)). With async SQLAlchemy + asyncpg, FastAPI can handle thousands of concurrent DB queries per second without thread pool exhaustion.
03
What is Flask's application factory pattern?
The application factory pattern creates the Flask app inside a function rather than at module level. This solves two problems: circular imports (extensions and blueprints import the app, but the app needs to import them) and testing (create app instances with different configs per test). Create a factory: def create_app(config=None): app = Flask(__name__); app.config.from_object(config or 'config.DefaultConfig'); db.init_app(app); app.register_blueprint(auth_bp); return app. Extensions like Flask-SQLAlchemy support this via db = SQLAlchemy() (without an app) and db.init_app(app). The factory is called in the entry point: app = create_app(). In tests: app = create_app('config.TestingConfig'); client = app.test_client(). This is the recommended pattern for any Flask app beyond a simple script.
04
How do you implement background tasks in FastAPI?
FastAPI provides BackgroundTasks for running lightweight tasks after returning the response. Inject in the route: @app.post('/send-email') async def send(background_tasks: BackgroundTasks, email: str): background_tasks.add_task(send_email_func, email); return {'msg': 'Email scheduled'}. The response is sent immediately; the background task runs after. Limitations: tasks run in the same process/thread pool — they block the worker from other requests. Not suitable for long-running or CPU-intensive tasks. For heavier background work: Celery (distributed task queue with Redis/RabbitMQ broker) — the standard for production background jobs in Flask/FastAPI. ARQ (async task queue built for asyncio). For simple scheduled jobs: APScheduler. Background tasks are appropriate for sending notifications, webhooks, and cache invalidation after a request completes.
05
What is Pydantic v2 and what changed from v1?
Pydantic v2 (released 2023) is a complete rewrite of the core validation logic in Rust via the pydantic-core package, resulting in 5-50x performance improvements. Key API changes: orm_mode renamed to model_config = ConfigDict(from_attributes=True). @validator replaced by @field_validator (with different signature). @root_validator replaced by @model_validator. Schema serialization: .dict() and .json() replaced by .model_dump() and .model_dump_json(). model_fields instead of __fields__. FastAPI 0.100+ supports Pydantic v2 with the new patterns. Most changes are backward-compatible with a compatibility layer; the pydantic.v1 module retains v1 behavior for gradual migration. Pydantic v2 also introduces @computed_field for derived properties and improved discriminated unions.
06
How do you implement middleware in FastAPI?
FastAPI (via Starlette) supports ASGI middleware. Built-in middleware: app.add_middleware(CORSMiddleware, ...), app.add_middleware(HTTPSRedirectMiddleware), app.add_middleware(TrustedHostMiddleware, allowed_hosts=["example.com"]). Custom middleware using Starlette's BaseHTTPMiddleware: class LoggingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): start = time.time(); response = await call_next(request); print(f"Request took {time.time()-start:.3f}s"); return response. Register: app.add_middleware(LoggingMiddleware). For pure ASGI middleware (lower level, more performant): wrap the app directly using the @app.middleware("http") decorator. Note: middleware wraps all requests including the auto-generated docs. Use dependency injection instead of middleware for route-specific concerns — it is more targeted and testable.
07
What is Alembic and how does it work with FastAPI?
Alembic is the database migration tool for SQLAlchemy. With FastAPI, use it directly (unlike Flask-Migrate which wraps it). Setup: pip install alembic; alembic init alembic. Configure alembic.ini with the database URL. Edit alembic/env.py to import your SQLAlchemy Base and metadata: from app.models import Base; target_metadata = Base.metadata. Commands: alembic revision --autogenerate -m "Add users table" (generate migration), alembic upgrade head (apply all migrations), alembic downgrade -1 (roll back one). Run migrations in the Docker entrypoint or app startup (use caution with multiple instances — only one should run migrations). Store migrations in version control. For async SQLAlchemy, configure Alembic to run migrations synchronously (connections in env.py use the sync engine for migrations even if the app uses async).
08
How do you write tests for Flask and FastAPI applications?
Flask testing: use the built-in test client: def test_home(client): response = client.get('/'); assert response.status_code == 200. Create a pytest fixture: @pytest.fixture def client(): app = create_app('testing'); with app.test_client() as c: yield c. FastAPI testing: use Starlette's TestClient (wraps httpx): from fastapi.testclient import TestClient; client = TestClient(app); def test_read(): response = client.get('/users/1'); assert response.status_code == 200. For async tests with httpx: async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.get('/'). Both frameworks support overriding dependencies in tests: FastAPI uses app.dependency_overrides to inject mocks. Always use a test database — configure via environment variables or fixtures.
09
What is rate limiting and how do you implement it in FastAPI?
Rate limiting protects your API from abuse and ensures fair use. For FastAPI, use slowapi (a FastAPI port of Flask-Limiter): from slowapi import Limiter; from slowapi.util import get_remote_address; limiter = Limiter(key_func=get_remote_address); app.state.limiter = limiter; app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler). Apply to routes: @app.get('/api/data') @limiter.limit('10/minute') async def get_data(request: Request): .... For production with distributed instances, use a Redis backend: Limiter(key_func=get_remote_address, storage_uri="redis://localhost:6379"). For advanced scenarios, implement custom middleware that increments a Redis counter with TTL. Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After) inform clients of their quota.
10
What are FastAPI WebSockets?
FastAPI supports WebSockets for persistent bidirectional connections. Define a WebSocket endpoint: @app.websocket('/ws') async def websocket_endpoint(websocket: WebSocket): await websocket.accept(); while True: data = await websocket.receive_text(); await websocket.send_text(f'Echo: {data}'). Handle disconnects: catch WebSocketDisconnect. For broadcasting to multiple connected clients, maintain a list of active connections and iterate to send. WebSocket dependencies work the same as HTTP dependencies — inject authentication, database sessions, etc. in the function signature with Depends(). FastAPI's WebSocket support is built on Starlette's, which uses websockets or wsproto under the hood. Use cases: real-time dashboards, collaborative editing, live chat, and streaming ML inference results.
11
What is the difference between Flask-RESTful and Flask-RESTX?
Flask-RESTful is an extension for building REST APIs with Flask, providing resource-based routing (class-based views where methods map to HTTP verbs), request parsing with reqparse, and response marshaling. Flask-RESTX is a fork and successor of Flask-RESTPlus, which extends Flask-RESTful with automatic Swagger documentation generation, namespaces (similar to Blueprints), and declarative API models. Flask-RESTX is the more feature-complete choice for documented REST APIs. However, both are less commonly used now compared to FastAPI, which provides all these features (auto-docs, validation, serialization) out of the box with better performance and type safety. Flask-RESTX is still a good choice if you have an existing Flask codebase and need to add Swagger documentation without a full migration to FastAPI.
12
How do you implement file uploads in FastAPI?
FastAPI handles file uploads with UploadFile and File: from fastapi import UploadFile, File; @app.post('/upload') async def upload(file: UploadFile = File(...)): contents = await file.read(); return {'name': file.filename, 'size': len(contents)}. For large files, avoid reading the entire file into memory — use file.file (SpooledTemporaryFile) and write in chunks or stream to cloud storage. Validate file type via file.content_type and extension. For multiple files: files: list[UploadFile] = File(...). Form data with files: mix UploadFile and Form fields in the same function signature. For production, upload directly to cloud storage (S3, GCS) rather than saving to local disk — use the aiobotocore or google-cloud-storage client in async context for streaming uploads without buffering the entire file.
13
What is Flask-Login and how does it work?
Flask-Login handles user session management for Flask applications. It manages logging in, logging out, and remembering users between sessions. Setup: login_manager = LoginManager(app); login_manager.login_view = 'auth.login'. Define the user loader: @login_manager.user_loader def load_user(user_id): return User.query.get(user_id). Login a user: login_user(user, remember=remember_me). Protect routes: @login_required — redirects to the login page if not authenticated. Get the current user: from flask_login import current_user. Logout: logout_user(). Flask-Login stores the user ID in the Flask session (signed cookie). Your User model must implement the UserMixin interface (is_authenticated, is_active, get_id()). It works best with session-based web apps; for API authentication, use JWT (Flask-JWT-Extended) instead.
14
How do you deploy FastAPI to production?
Production FastAPI deployment options: Docker + Gunicorn + Uvicorn workers: the standard pattern. Dockerfile: base on tiangolo/uvicorn-gunicorn-fastapi image or build custom. Run: gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000. Kubernetes (AKS/GKE/EKS): containerize, create Deployment + Service + Ingress. Use Horizontal Pod Autoscaler. Serverless containers: Cloud Run, Azure Container Apps, AWS App Runner — zero config, scale to zero. Serverless functions: AWS Lambda with Mangum adapter (converts ASGI to Lambda handler). Checklist: disable debug mode, set workers proportional to CPU cores, configure trusted hosts, add rate limiting, enable HTTPS via reverse proxy (Nginx/Traefik), configure structured logging (JSON for log aggregation), add health check endpoint (/health), run as non-root user in Docker.
Deep expertise questions for senior and lead roles.
01
What is FastAPI's OpenAPI specification and how is it customized?
FastAPI auto-generates an OpenAPI 3.0 spec from your code at /openapi.json and serves interactive UI at /docs (Swagger UI) and /redoc. Customize the spec: app = FastAPI(title="My API", description="Detailed description", version="2.0.0", docs_url="/api-docs", redoc_url=None). Enrich endpoints with metadata: @app.get("/items", tags=["items"], summary="List all items", description="Returns paginated list", response_description="List of items", deprecated=False). Add security schemes: app = FastAPI(); app.openapi_schema = custom_schema or override app.openapi() for full control. Add examples to Pydantic models with model_config = {"json_schema_extra": {"examples": [...]}}. Generate client code from the spec using openapi-generator or fastapi-code-generator. The spec can also be exported to Postman collections for team sharing.
02
How do you implement event-driven patterns in FastAPI with Kafka or Redis?
For event-driven patterns in FastAPI: Kafka with aiokafka: producer = AIOKafkaProducer(bootstrap_servers='localhost:9092'); await producer.start(). Publish events after handling requests: await producer.send('user-created', value=json.dumps(user_data).encode()). Consume in a background task started with app.on_event("startup"): asyncio.create_task(consume()). Redis Pub/Sub with aioredis: subscribe to channels for real-time messaging. Lifespan events (FastAPI 0.95+): @asynccontextmanager async def lifespan(app): # startup: connect producer, start consumer task; yield; # shutdown: stop consumer, close connections; app = FastAPI(lifespan=lifespan). This pattern is cleaner than deprecated @app.on_event. For outbox pattern: write events to DB atomically with business data, then publish from DB via Debezium CDC.
03
What are advanced Pydantic features for complex validation?
Advanced Pydantic v2 validation: Field validators: @field_validator('password') @classmethod def password_strength(cls, v): if len(v) < 8: raise ValueError('too short'); return v. Model validators: validate across multiple fields: @model_validator(mode='after') def check_password_match(self): if self.password != self.password_confirm: raise ValueError('Passwords do not match'); return self. Discriminated unions: Union[Cat, Dog] with a discriminator field for efficient deserialization. Custom types: subclass str with __get_validators__. Serialization modes: model_dump(mode='json') for JSON-serializable output. Aliases: Field(alias='user_name') for external API compatibility. Computed fields: @computed_field @property def full_name(self) -> str: return f'{self.first} {self.last}'. These features make Pydantic a comprehensive data modeling layer.
04
How do you implement a multi-tenant API with FastAPI?
Multi-tenancy in FastAPI at the API level: Subdomain-based: extract tenant from host header in middleware, inject into request state. Header-based: clients send X-Tenant-ID header; extract in dependency. JWT-claim-based: include tenant_id in the JWT; extract when authenticating. Dependency pattern: async def get_tenant(tenant_id: str = Header(...)) -> Tenant: tenant = await Tenant.get(tenant_id); if not tenant: raise HTTPException(404); return tenant. Database strategies: Row-level: filter every query by tenant_id — use SQLAlchemy query filters automatically applied via a custom session or base query. Schema-level (PostgreSQL): switch schema via SET search_path TO tenant_schema at the connection level — use a scoped DB dependency that sets the schema. Ensure every query is tenant-scoped to prevent cross-tenant data leakage — this is the most critical security requirement.
05
What is the strangler fig pattern for migrating Flask to FastAPI?
The strangler fig pattern migrates a Flask API to FastAPI incrementally without a big-bang rewrite. Strategy: 1. Set up a reverse proxy (Nginx) in front of both Flask and FastAPI apps running simultaneously. 2. New endpoints: implement in FastAPI. 3. Migrate existing endpoints one by one from Flask to FastAPI — route each migrated path to FastAPI in Nginx. 4. Gradually strangle Flask as more routes move to FastAPI. Mounting apps: FastAPI can mount a WSGI app: from a2wsgi import WSGIMiddleware; app.mount('/legacy', WSGIMiddleware(flask_app)). This lets FastAPI and Flask share the same process — FastAPI handles new routes, Flask handles legacy routes. Share a database connection pool and authentication logic. Eventually, all routes are in FastAPI and the Flask app is removed. This approach minimizes risk by never having a complete application rewrite that must be deployed atomically.
06
How does FastAPI handle streaming responses?
FastAPI supports streaming responses for sending large data incrementally. StreamingResponse: from fastapi.responses import StreamingResponse; async def generate(): for chunk in large_data: yield chunk; @app.get('/stream') async def stream(): return StreamingResponse(generate(), media_type='application/octet-stream'). For Server-Sent Events (SSE): async def event_generator(): while True: data = await get_update(); yield f'data: {json.dumps(data)}\n\n'; return StreamingResponse(event_generator(), media_type='text/event-stream', headers={'Cache-Control': 'no-cache'}). For streaming LLM responses: stream tokens from the model and yield them — the client sees words appearing as they are generated. File streaming: use FileResponse for files or StreamingResponse with a file iterator for large files to avoid loading into memory. Streaming is essential for progress feedback, live logs, and reducing time-to-first-byte for large responses.
07
What is dependency injection with classes and factories in FastAPI?
FastAPI's dependency system supports three patterns. Functions (basic): def get_db(): yield session. Classes with __call__: create stateful, configurable dependencies: class PaginationParams: def __init__(self, page: int = 1, size: int = 20): self.page = page; self.size = size; @app.get('/items') async def list_items(pagination: PaginationParams = Depends()): ... — FastAPI instantiates the class with query params automatically when using Depends() without arguments on a class. Factory functions: create dependencies with configuration: def get_permission_checker(required: str): async def check(user: User = Depends(get_user)): if required not in user.permissions: raise HTTPException(403); return user; return check. Usage: @app.delete('/admin', dependencies=[Depends(get_permission_checker('admin'))]). This enables reusable, composable authorization dependencies with parameterized behavior.
08
How does FastAPI integrate with GraphQL?
FastAPI integrates with Strawberry (the recommended library) for GraphQL: import strawberry; from strawberry.fastapi import GraphQLRouter; @strawberry.type class Query: @strawberry.field def hello(self) -> str: return "world"; schema = strawberry.Schema(Query); graphql_app = GraphQLRouter(schema); app.include_router(graphql_app, prefix="/graphql"). Strawberry uses Python type annotations with its own decorators. Context injection: pass the request/user to resolvers via context: async def get_context(request: Request) -> Context: return Context(request=request, user=await get_current_user(request)). Async resolvers work natively. Subscriptions over WebSocket are supported. DataLoader (batching) solves N+1 queries. Alternatively, use Ariadne (schema-first) or Graphene (older, code-first). FastAPI + Strawberry is the modern Python GraphQL stack — it benefits from FastAPI's performance, dependency injection, and Swagger UI (for HTTP layer) alongside GraphQL's flexible queries.
09
What is the repository pattern and how is it implemented in FastAPI?
The repository pattern abstracts database access behind an interface, keeping route handlers and business logic independent of the database technology. Implementation: class UserRepository: def __init__(self, db: AsyncSession): self.db = db; async def get_by_id(self, user_id: int) -> User | None: result = await self.db.execute(select(User).where(User.id == user_id)); return result.scalar_one_or_none(); async def create(self, data: UserCreate) -> User: user = User(**data.model_dump()); self.db.add(user); await self.db.commit(); await self.db.refresh(user); return user. Create a FastAPI dependency: def get_user_repo(db: AsyncSession = Depends(get_db)) -> UserRepository: return UserRepository(db). Use in route: @app.post('/users') async def create_user(data: UserCreate, repo: UserRepository = Depends(get_user_repo)): return await repo.create(data). Benefits: swap DB without changing routes, mock the repository in tests, keep queries organized per entity.