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

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

JSP Servlet Interview Questions For Freshers Series 1

Java program to check even or odd number