FastAPI Crash Course: Modern, Fast Python APIs

Part 3 of the Python for Web & APIs track. Last updated: September 2026.

Flask taught you how APIs work. FastAPI is how most new Python APIs get built: you write plain Python with type hints, and FastAPI generates request validation, serialization, and interactive documentation automatically. It is fast (on par with Node.js in benchmarks), async-native, and the code reads like documentation. This post rebuilds the book catalog from Part 2 the FastAPI way.

Setup

python -m venv venv
source venv/bin/activate      # Windows: venv\Scripts\activate
pip install fastapi "uvicorn[standard]"

Your first FastAPI app

Type hints are the whole game — they declare what each endpoint accepts:

from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
def hello():
    return {"message": "Hello from FastAPI!"}

@app.get("/books/{book_id}")
def get_book(book_id: int):       # FastAPI validates + converts book_id
    return {"id": book_id, "title": f"Book #{book_id}"}

Run it with uvicorn main:app --reload (assuming the file is main.py). Request /books/abc and you get a 422 Unprocessable Entity with a precise JSON error — validation happened with zero code from you. Now visit http://127.0.0.1:8000/docs: a full interactive API console (Swagger UI) generated from your code. Try the endpoints right in the browser.

Query parameters and validation

Declare them as function arguments with defaults and constraints:

from fastapi import Query

@app.get("/search")
def search(
    author: str = "",
    limit: int = Query(default=10, ge=1, le=100),   # 1 <= limit <= 100
):
    return {"author": author, "limit": limit}

?limit=500 now returns a 422 explaining the constraint. That manual validate_book() function from Part 2? Gone.

Pydantic models: the heart of FastAPI

Request and response shapes are Pydantic models — classes with typed fields:

from pydantic import BaseModel, Field

class BookIn(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    author: str = Field(min_length=1, max_length=100)
    year: int | None = Field(default=None, ge=0, le=2100)

class Book(BookIn):
    id: int

Send {"title": "", "year": "not-a-number"} and FastAPI rejects it with a 422 listing every problem. Invalid data can no longer reach your code — the framework stands guard at the door.

Project: the book catalog, FastAPI edition

The same API as Part 2 — notice how much validation code disappears:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field

app = FastAPI(title="Book Catalog API")

class BookIn(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    author: str = Field(min_length=1, max_length=100)
    year: int | None = Field(default=None, ge=0, le=2100)

class Book(BookIn):
    id: int

books: dict[int, Book] = {
    1: Book(id=1, title="Dune", author="Frank Herbert", year=1965),
    2: Book(id=2, title="Neuromancer", author="William Gibson", year=1984),
}
next_id = 3

@app.get("/books", response_model=list[Book])
def list_books(author: str = ""):
    result = list(books.values())
    if author:
        result = [b for b in result if author.lower() in b.author.lower()]
    return result

@app.get("/books/{book_id}", response_model=Book)
def get_book(book_id: int):
    if book_id not in books:
        raise HTTPException(status_code=404, detail=f"No book with id {book_id}")
    return books[book_id]

@app.post("/books", response_model=Book, status_code=201)
def create_book(payload: BookIn):
    global next_id
    book = Book(id=next_id, **payload.model_dump())
    books[next_id] = book
    next_id += 1
    return book

@app.put("/books/{book_id}", response_model=Book)
def update_book(book_id: int, payload: BookIn):
    if book_id not in books:
        raise HTTPException(status_code=404, detail=f"No book with id {book_id}")
    book = Book(id=book_id, **payload.model_dump())
    books[book_id] = book
    return book

@app.delete("/books/{book_id}", status_code=204)
def delete_book(book_id: int):
    if book_id not in books:
        raise HTTPException(status_code=404, detail=f"No book with id {book_id}")
    del books[book_id]
    return None

Three things to notice:

  • response_model — FastAPI filters every response through the model, so you never leak internal fields.
  • HTTPException — the idiomatic way to return errors; FastAPI renders them as clean JSON.
  • Status codes as decorators — status_code=201 on the route, no tuple juggling.

Async endpoints (free performance)

Declare a route with async def and it runs on the event loop — while one request waits on a database or another API, others proceed:

import httpx

@app.get("/books/{book_id}/cover")
async def book_cover(book_id: int):
    async with httpx.AsyncClient(timeout=10) as client:
        # placeholder: pretend an external cover-art service exists
        r = await client.get(f"https://api.example.com/covers/{book_id}")
        r.raise_for_status()
        return {"book_id": book_id, "cover": r.json()}

Rule of thumb: async def for I/O-bound work (network, DB calls); plain def is fine for CPU-bound code (FastAPI runs those in a threadpool).

Flask vs FastAPI: which to choose?

  • FastAPI — new APIs, teams, anything where validation and docs matter. Automatic 422s, OpenAPI docs, async. The modern default.
  • Flask — tiny services, maximum control, huge extension ecosystem, and learning how the web works (which is why this track starts there).

Both are production-grade. The skills transfer: routes, status codes, and JSON discipline are the same everywhere.

Key takeaways

  • Type hints are executable: they give you validation, conversion, and docs for free.
  • Pydantic models define your API contract — bad input gets a 422 before reaching your code.
  • response_model guarantees your output shape; HTTPException is how you raise clean JSON errors.
  • /docs gives you interactive documentation with zero extra work — use it, your API consumers will love you.
  • async def routes handle I/O-bound waiting efficiently.

Next in this series: Testing and Deploying Your Python API — prove it works with automated tests, then put it on the real internet.

Comments

Popular posts from this blog

What are the core classes and interfaces of hibernate 4

JSP Servlet Interview Questions For Freshers Series 1

Java Banking Finance Services and Insurance (BFSI) domain interview questions