We use cookies to enhance your experience on the site
CodeWorlds

FastAPI - modern asynchronous API

Welcome back, @name! Darwin here with FastAPI - the most modern framework for building APIs in Python! 🚀⚡

In the previous lesson you learned async/await. Now we'll use that knowledge with FastAPI - a framework that is 3-5x faster than Flask, has automatic validation, and generates documentation automatically! 🌟

Safari Analogy: Flask is a traditional safari tour - tried and true, reliable. FastAPI is a safari with drones - fast, modern, with GPS and automatic animal tracking! 🚁🦁

What is FastAPI?

FastAPI is a modern, fast framework for building APIs created by Sebastian Ramirez in 2018.

Key features:Fast - comparable to Node.js/Go (thanks to Starlette) ✅ Async native - full support for async/await ✅ Type hints - uses type annotations ✅ Automatic validation - Pydantic out-of-the-box ✅ Auto docs - Swagger UI + ReDoc automatically ✅ Modern Python - Python 3.7+ required

Installing FastAPI

1pip install fastapi[all]  # Everything (Uvicorn, Pydantic, etc.)
2
3# Or minimally:
4pip install fastapi uvicorn[standard]

Uvicorn = ASGI server (like Gunicorn for WSGI)

Hello World FastAPI

1# main.py
2from fastapi import FastAPI
3
4app = FastAPI()
5
6@app.get("/")
7async def root():
8    return {"message": "Hello Safari! 🦁"}
9
10# Run: uvicorn main:app --reload

Run:

1uvicorn main:app --reload

Output:

1INFO: Uvicorn running on http://127.0.0.1:8000
2INFO: Application startup complete.

Open:

  • http://localhost:8000/ → {"message": "Hello Safari! 🦁"}
  • http://localhost:8000/docsAutomatic Swagger UI documentation! 📘
  • http://localhost:8000/redoc → Alternative ReDoc documentation

FastAPI vs Flask

| FastAPI | Flask | |---------|-------| | Async native | Sync (async possible, but difficult) | | Type hints required | Optional | | Automatic validation (Pydantic) | Manual (Flask-WTF) | | Auto docs (Swagger/ReDoc) | None | | 3-5x faster | Slower | | Modern Python (3.7+) | Older versions OK | | API-first | Web apps + API |

When FastAPI? REST API, microservices, async I/O When Flask? Web apps with templates, smaller projects

Basic endpoints

1from fastapi import FastAPI
2
3app = FastAPI(title="Safari Database API", version="1.0.0")
4
5# GET - fetch data
6@app.get("/species")
7async def list_species():
8    return [
9        {"id": 1, "name": "Lion", "population": 120},
10        {"id": 2, "name": "Elephant", "population": 450}
11    ]
12
13# GET with path parameter
14@app.get("/species/{species_id}")
15async def get_species(species_id: int):
16    return {"id": species_id, "name": "Lion", "population": 120}
17
18# POST - add data
19@app.post("/species")
20async def create_species(name: str, population: int):
21    return {"id": 3, "name": name, "population": population, "status": "created"}
22
23# PUT - update
24@app.put("/species/{species_id}")
25async def update_species(species_id: int, name: str, population: int):
26    return {"id": species_id, "name": name, "population": population}
27
28# DELETE - remove
29@app.delete("/species/{species_id}")
30async def delete_species(species_id: int):
31    return {"id": species_id, "status": "deleted"}

Test:

1# GET
2curl http://localhost:8000/species
3
4# POST
5curl -X POST "http://localhost:8000/species?name=Cheetah&population=7100"

Query parameters - automatic validation

FastAPI automatically parses query params and validates types!

1from fastapi import FastAPI
2
3app = FastAPI()
4
5@app.get("/species")
6async def list_species(
7    skip: int = 0,
8    limit: int = 10,
9    habitat: str | None = None,
10    endangered: bool = False
11):
12    # FastAPI automatically:
13    # - parses ?skip=10&limit=5&habitat=savanna&endangered=true
14    # - converts types (str → int, str → bool)
15    # - validates (skip must be int!)
16
17    return {
18        "skip": skip,
19        "limit": limit,
20        "habitat": habitat,
21        "endangered": endangered,
22        "results": ["Lion", "Elephant"]
23    }

Test:

1/species                              → skip=0, limit=10, habitat=None, endangered=False
2/species?skip=20&limit=5              → skip=20, limit=5
3/species?habitat=savanna&endangered=1 → habitat="savanna", endangered=True
4/species?skip=abc                     → ERROR: validation error!

FastAPI automatically generates validation errors:

1{
2  "detail": [
3    {
4      "loc": ["query", "skip"],
5      "msg": "value is not a valid integer",
6      "type": "type_error.integer"
7    }
8  ]
9}

Response models - structured responses

1from fastapi import FastAPI
2from pydantic import BaseModel
3
4app = FastAPI()
5
6class Species(BaseModel):
7    id: int
8    name: str
9    scientific_name: str
10    population: int
11    endangered: bool = False
12
13@app.get("/species/{species_id}", response_model=Species)
14async def get_species(species_id: int):
15    return Species(
16        id=species_id,
17        name="Lion",
18        scientific_name="Panthera leo",
19        population=120,
20        endangered=True
21    )

Benefits of

response_model
:

  • Automatic serialization to JSON
  • Output validation
  • Documentation automatically shows the structure
  • Type safety

Full Safari API - example

1# main.py
2from fastapi import FastAPI, HTTPException
3from pydantic import BaseModel, Field
4from typing import List
5
6app = FastAPI(
7    title="Safari Database API",
8    description="API for managing African wildlife species",
9    version="1.0.0"
10)
11
12# Models
13class SpeciesBase(BaseModel):
14    name: str = Field(..., min_length=2, max_length=100)
15    scientific_name: str
16    population: int = Field(..., ge=0)
17    habitat: str
18
19class SpeciesCreate(SpeciesBase):
20    pass
21
22class Species(SpeciesBase):
23    id: int
24
25    class Config:
26        from_attributes = True
27
28# In-memory database
29species_db: dict[int, Species] = {
30    1: Species(id=1, name="Lion", scientific_name="Panthera leo", population=120, habitat="savanna"),
31    2: Species(id=2, name="Elephant", scientific_name="Loxodonta africana", population=450, habitat="savanna"),
32}
33next_id = 3
34
35# Endpoints
36@app.get("/")
37async def root():
38    return {"message": "Safari Database API", "version": "1.0.0"}
39
40@app.get("/species", response_model=List[Species])
41async def list_species(skip: int = 0, limit: int = 10, habitat: str | None = None):
42    """List all species with pagination"""
43    results = list(species_db.values())
44
45    if habitat:
46        results = [s for s in results if s.habitat == habitat]
47
48    return results[skip : skip + limit]
49
50@app.get("/species/{species_id}", response_model=Species)
51async def get_species(species_id: int):
52    """Get specific species by ID"""
53    if species_id not in species_db:
54        raise HTTPException(status_code=404, detail=f"Species {species_id} not found")
55
56    return species_db[species_id]
57
58@app.post("/species", response_model=Species, status_code=201)
59async def create_species(species: SpeciesCreate):
60    """Create new species"""
61    global next_id
62
63    new_species = Species(id=next_id, **species.dict())
64    species_db[next_id] = new_species
65    next_id += 1
66
67    return new_species
68
69@app.put("/species/{species_id}", response_model=Species)
70async def update_species(species_id: int, species: SpeciesCreate):
71    """Update existing species"""
72    if species_id not in species_db:
73        raise HTTPException(status_code=404, detail="Species not found")
74
75    updated = Species(id=species_id, **species.dict())
76    species_db[species_id] = updated
77
78    return updated
79
80@app.delete("/species/{species_id}")
81async def delete_species(species_id: int):
82    """Delete species"""
83    if species_id not in species_db:
84        raise HTTPException(status_code=404, detail="Species not found")
85
86    del species_db[species_id]
87    return {"status": "deleted", "id": species_id}

Run:

1uvicorn main:app --reload

Test the API:

1# List
2curl http://localhost:8000/species
3
4# Specific species
5curl http://localhost:8000/species/1
6
7# Add
8curl -X POST http://localhost:8000/species \
9  -H "Content-Type: application/json" \
10  -d '{"name":"Cheetah","scientific_name":"Acinonyx jubatus","population":7100,"habitat":"savanna"}'
11
12# Update
13curl -X PUT http://localhost:8000/species/1 \
14  -H "Content-Type: application/json" \
15  -d '{"name":"Lion","scientific_name":"Panthera leo","population":150,"habitat":"savanna"}'
16
17# Delete
18curl -X DELETE http://localhost:8000/species/3

Automatic documentation

FastAPI automatically generates interactive documentation!

Swagger UI: http://localhost:8000/docs

  • Interactive API testing
  • "Try it out" button for each endpoint
  • Automatic forms

ReDoc: http://localhost:8000/redoc

  • Readable documentation
  • Great for developers

OpenAPI JSON: http://localhost:8000/openapi.json

  • Raw OpenAPI 3.0 schema
  • For external tools

Summary

In this lesson you learned:

  • ✅ What FastAPI is and why to use it
  • ✅ Installing FastAPI + Uvicorn
  • ✅ Hello World and basic endpoints
  • ✅ GET, POST, PUT, DELETE
  • ✅ Path parameters and query parameters
  • ✅ Response models with Pydantic
  • ✅ HTTPException for errors
  • ✅ Automatic documentation (Swagger/ReDoc)
  • ✅ Full Safari Database API

Next lesson: Darwin will show you Pydantic v2 - a powerful data validation system with FastAPI! 📊✅

Go to CodeWorlds