FroquizFroquiz
HomeQuizzesSenior ChallengeGet CertifiedBlogAbout
Sign InStart Quiz
Sign InStart Quiz
Froquiz

The most comprehensive quiz platform for software engineers. Test yourself with 10000+ questions and advance your career.

LinkedIn

Platform

  • Start Quizzes
  • Topics
  • Blog
  • My Profile
  • Sign In

About

  • About Us
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

Β© 2026 Froquiz. All rights reserved.Built with passion for technology
Blog & Articles

FastAPI: Building High-Performance Python APIs with Type Safety

Learn FastAPI from scratch. Covers path operations, Pydantic models, dependency injection, authentication, async database access, background tasks, and automatic documentation.

Yusuf SeyitoğluMarch 17, 20261 views10 min read

FastAPI: Building High-Performance Python APIs with Type Safety

FastAPI has become the go-to framework for new Python APIs. It is fast (one of the fastest Python frameworks, on par with NodeJS and Go in benchmarks), developer-friendly (automatic interactive docs, full type checking), and modern (async-first, Pydantic validation). If you know Django or Flask and have not tried FastAPI yet, this guide will show you why it has taken the Python world by storm.

Why FastAPI?

  • Automatic OpenAPI docs β€” Swagger UI and ReDoc generated from your code
  • Pydantic validation β€” request bodies validated and parsed automatically from type hints
  • Async-first β€” built on Starlette and uvicorn, handles concurrency natively
  • Dependency injection β€” clean, testable code through DI system
  • Editor support β€” full autocompletion and type checking because of standard Python types

Setup

bash
pip install fastapi uvicorn[standard] sqlalchemy asyncpg python-jose[cryptography] passlib[bcrypt] uvicorn main:app --reload

Basic Application

python
from fastapi import FastAPI from pydantic import BaseModel from typing import Optional app = FastAPI( title="My API", description="A FastAPI example", version="1.0.0", ) class Item(BaseModel): name: str price: float description: Optional[str] = None in_stock: bool = True items_db: dict[int, Item] = {} counter = 0 @app.get("/") async def root(): return {"message": "Hello World"} @app.get("/items/{item_id}", response_model=Item) async def get_item(item_id: int): if item_id not in items_db: raise HTTPException(status_code=404, detail="Item not found") return items_db[item_id] @app.post("/items/", response_model=Item, status_code=201) async def create_item(item: Item): global counter counter += 1 items_db[counter] = item return item @app.put("/items/{item_id}", response_model=Item) async def update_item(item_id: int, item: Item): if item_id not in items_db: raise HTTPException(status_code=404, detail="Item not found") items_db[item_id] = item return item @app.delete("/items/{item_id}", status_code=204) async def delete_item(item_id: int): if item_id not in items_db: raise HTTPException(status_code=404, detail="Item not found") del items_db[item_id]

Pydantic Models

Pydantic v2 is the backbone of FastAPI validation:

python
from pydantic import BaseModel, EmailStr, field_validator, model_validator from typing import Optional from datetime import datetime import re class UserCreate(BaseModel): username: str email: EmailStr password: str age: Optional[int] = None @field_validator("username") @classmethod def username_alphanumeric(cls, v: str) -> str: if not re.match(r"^[a-zA-Z0-9_]+$", v): raise ValueError("Username must be alphanumeric") if len(v) < 3: raise ValueError("Username must be at least 3 characters") return v.lower() @field_validator("password") @classmethod def password_strength(cls, v: str) -> str: if len(v) < 8: raise ValueError("Password must be at least 8 characters") return v class UserResponse(BaseModel): id: int username: str email: str created_at: datetime model_config = {"from_attributes": True} -- enables ORM mode class UserUpdate(BaseModel): username: Optional[str] = None email: Optional[EmailStr] = None model_config = {"extra": "forbid"} -- reject unknown fields

Query Parameters and Path Parameters

python
from fastapi import Query, Path @app.get("/users/") async def list_users( page: int = Query(default=1, ge=1, description="Page number"), per_page: int = Query(default=20, ge=1, le=100), search: str = Query(default="", max_length=100), active: bool = Query(default=True), ): offset = (page - 1) * per_page return {"page": page, "per_page": per_page, "search": search} @app.get("/users/{user_id}") async def get_user( user_id: int = Path(ge=1, description="The user ID"), ): return {"user_id": user_id}

Dependency Injection

FastAPI's DI system is one of its most powerful features:

python
from fastapi import Depends, Header, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from .database import async_session -- Database session dependency async def get_db(): async with async_session() as session: try: yield session finally: await session.close() -- Authentication dependency async def get_current_user( authorization: str = Header(...), db: AsyncSession = Depends(get_db), ) -> User: if not authorization.startswith("Bearer "): raise HTTPException(status_code=401, detail="Invalid authorization header") token = authorization[7:] try: payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) user_id = payload.get("sub") except JWTError: raise HTTPException(status_code=401, detail="Invalid token") user = await db.get(User, int(user_id)) if not user: raise HTTPException(status_code=401, detail="User not found") return user -- Admin dependency built on top of auth async def require_admin(current_user: User = Depends(get_current_user)) -> User: if not current_user.is_admin: raise HTTPException(status_code=403, detail="Admin access required") return current_user -- Use in routes @app.get("/me", response_model=UserResponse) async def get_me(current_user: User = Depends(get_current_user)): return current_user @app.delete("/users/{user_id}") async def delete_user( user_id: int, db: AsyncSession = Depends(get_db), _: User = Depends(require_admin), -- enforces admin without using the value ): ...

Async Database Access with SQLAlchemy

python
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship from sqlalchemy import select, String, Integer, ForeignKey from datetime import datetime engine = create_async_engine( "postgresql+asyncpg://user:pass@localhost/dbname", echo=False, ) async_session = async_sessionmaker(engine, expire_on_commit=False) class Base(DeclarativeBase): pass class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(Integer, primary_key=True) username: Mapped[str] = mapped_column(String(50), unique=True) email: Mapped[str] = mapped_column(String(200), unique=True) created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) posts: Mapped[list["Post"]] = relationship(back_populates="author") -- Repository pattern class UserRepository: def __init__(self, db: AsyncSession): self.db = db async def get_by_id(self, user_id: int) -> User | None: return await self.db.get(User, user_id) async def get_by_email(self, email: str) -> User | None: result = await self.db.execute( select(User).where(User.email == email) ) return result.scalar_one_or_none() async def create(self, data: UserCreate) -> User: user = User( username=data.username, email=data.email, password_hash=hash_password(data.password), ) self.db.add(user) await self.db.commit() await self.db.refresh(user) return user

Background Tasks

python
from fastapi import BackgroundTasks import logging def send_welcome_email(email: str, username: str): -- Runs after the response is sent logging.info(f"Sending welcome email to {email}") email_service.send( to=email, subject="Welcome!", body=f"Hi {username}, welcome to our platform!", ) @app.post("/users/", response_model=UserResponse, status_code=201) async def create_user( user_data: UserCreate, background_tasks: BackgroundTasks, db: AsyncSession = Depends(get_db), ): user = await UserRepository(db).create(user_data) background_tasks.add_task(send_welcome_email, user.email, user.username) return user -- Response is sent immediately; email sends in background

Routers (Blueprint equivalent)

python
-- routers/users.py from fastapi import APIRouter router = APIRouter(prefix="/users", tags=["users"]) @router.get("/", response_model=list[UserResponse]) async def list_users(db: AsyncSession = Depends(get_db)): ... @router.get("/{user_id}", response_model=UserResponse) async def get_user(user_id: int, db: AsyncSession = Depends(get_db)): ... -- main.py from routers import users, posts, auth app = FastAPI() app.include_router(auth.router, prefix="/auth") app.include_router(users.router) app.include_router(posts.router)

Error Handling

python
from fastapi import Request from fastapi.responses import JSONResponse class AppException(Exception): def __init__(self, status_code: int, detail: str): self.status_code = status_code self.detail = detail @app.exception_handler(AppException) async def app_exception_handler(request: Request, exc: AppException): return JSONResponse( status_code=exc.status_code, content={"error": exc.detail}, ) -- Validation errors are handled automatically by FastAPI -- They return 422 Unprocessable Entity with field-level details

Testing FastAPI

python
from fastapi.testclient import TestClient from httpx import AsyncClient import pytest -- Synchronous tests client = TestClient(app) def test_create_user(): response = client.post("/users/", json={ "username": "alice", "email": "alice@example.com", "password": "securepass123", }) assert response.status_code == 201 assert response.json()["username"] == "alice" def test_create_user_duplicate_email(): client.post("/users/", json={"username": "a", "email": "dup@test.com", "password": "pass1234"}) response = client.post("/users/", json={"username": "b", "email": "dup@test.com", "password": "pass1234"}) assert response.status_code == 409 -- Async tests @pytest.mark.asyncio async def test_async(): async with AsyncClient(app=app, base_url="http://test") as client: response = await client.get("/users/") assert response.status_code == 200

Common Interview Questions

Q: How does FastAPI compare to Flask and Django REST Framework?

Flask is minimal β€” you add everything (validation, serialization, docs) yourself. Django REST Framework has more batteries included but is heavier and synchronous by default. FastAPI is async-first, automatically generates OpenAPI docs from type hints, validates with Pydantic, and is significantly faster than both. For new Python APIs, FastAPI is usually the best choice.

Q: What is Pydantic and why is it central to FastAPI?

Pydantic provides data validation and serialization using Python type annotations. FastAPI uses Pydantic models to parse and validate request bodies, query parameters, and path parameters automatically. If validation fails, FastAPI returns a 422 response with field-level error details β€” no manual validation code needed.

Q: What is the difference between async def and def route handlers in FastAPI?

Both work. async def handlers run in the async event loop and should use async libraries (asyncpg, httpx, aiofiles). def handlers run in a thread pool so they do not block the event loop. Use async def for I/O-bound async operations; use def for CPU-bound work or when using blocking libraries.

Practice Python on Froquiz

FastAPI and Python backend patterns are tested in Python developer interviews. Test your Python skills on Froquiz β€” covering async, OOP, decorators, and data structures.

Summary

  • FastAPI generates OpenAPI/Swagger docs automatically from type hints and Pydantic models
  • Pydantic validates request bodies, query params, and path params β€” 422 on invalid input
  • Dependency injection via Depends() keeps routes clean and testable
  • Use async def for async I/O; def for blocking/CPU work (runs in thread pool)
  • BackgroundTasks runs work after the response is sent β€” ideal for emails, logging
  • APIRouter organizes routes into modules β€” each router has its own prefix and tags
  • TestClient for sync tests; AsyncClient from httpx for async integration tests

About Author

Yusuf Seyitoğlu

Author β†’

Other Posts

  • Modern JavaScript: ES2020 to ES2024 Features You Should Be UsingMar 17
  • Software Architecture Patterns: MVC, Clean Architecture, Hexagonal and Event-DrivenMar 17
  • PostgreSQL Full-Text Search: tsvector, tsquery, Ranking and Multilingual SearchMar 17
All Blogs