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! 🚁🦁
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
1pip install fastapi[all] # Everything (Uvicorn, Pydantic, etc.)
2
3# Or minimally:
4pip install fastapi uvicorn[standard]Uvicorn = ASGI server (like Gunicorn for WSGI)
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 --reloadRun:
1uvicorn main:app --reloadOutput:
1INFO: Uvicorn running on http://127.0.0.1:8000
2INFO: Application startup complete.Open:
| 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
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"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}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
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 --reloadTest 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/3FastAPI automatically generates interactive documentation!
Swagger UI: http://localhost:8000/docs
ReDoc: http://localhost:8000/redoc
OpenAPI JSON: http://localhost:8000/openapi.json
In this lesson you learned:
Next lesson: Darwin will show you Pydantic v2 - a powerful data validation system with FastAPI! 📊✅