In the ground since Wed Feb 24 2021
Last watered inWed Feb 24 2021
Fast API
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.8+ based on standard Python type hints.
Benefits
- Fast: Very high performance, on par with NodeJS and Go.
- Type-checked (catches errors before runtime)
- Automatic API documentation
Anatomy of a FastAPI application
1@router.post("/", response_model=User)
2def create_user(
3 user_in: UserCreate,
4 db: Session = Depends(get_db)
5):
6
7This is a decorator that tells FastAPI this function handles POST requests
8```python
9@router.post("/")
Defines what the API will return (automatic validation!)
Input validation using Pydantic models
Dependency injection (we'll cover this later)
Return values
- FastAPI automatically converts Python dictionaries to JSON
- Content-Type header will be application/json
1return {"message": "User created successfully"}
Path parameters
1@router.get("/users/{user_id}")
Synchronous vs Asynchronous in FastAPI
FastAPI supports both synchronous and asynchronous endpoints. Understanding the difference is crucial for building efficient APIs.
Synchronous (Sync) Endpoints
1@app.get("/users")
2def get_users():
3 # Blocks here until database returns
4 result = db.query(User).all()
5 return result
Characteristics:
- Executes code line by line
- Waits for each operation to complete
- Blocks the thread while waiting
- Simpler to understand and debug
Asynchronous (Async) Endpoints
1@app.get("/users")
2async def get_users():
3 # Releases thread while waiting for database
4 result = await db.query(User).all()
5 return result
Characteristics:
- Can handle multiple operations concurrently
- Doesn't block while waiting for I/O
- More efficient for I/O-bound operations
- Uses async/await syntax
When to Use Each
Use Sync when:
- Doing CPU-intensive work
- Using libraries that don't support async
- Code is simple and doesn't involve waiting
1@app.get("/calculate")
2def calculate_something():
3 result = heavy_computation() # CPU-intensive
4 return {"result": result}
Use Async when:
- Making HTTP requests
- Database operations (with async driver)
- File I/O operations
- Handling websockets
1@app.get("/fetch-external")
2async def fetch_external_api():
3 async with httpx.AsyncClient() as client:
4 response = await client.get("https://api.example.com")
5 return response.json()
Common Pitfalls
- Blocking I/O in Async Functions (Bad)
1@app.get("/slow")
2async def slow_endpoint():
3 time.sleep(1) # Blocks the thread!
4 return {"message": "Done"}
- Proper Async Usage (Good)
1@app.get("/fast")
2async def fast_endpoint():
3 await asyncio.sleep(1) # Releases thread while waiting
4 return {"message": "Done"}
API Documentation
FastAPI provides automatic interactive API documentation using:
Swagger UI
- Available at /docs
- Interactive documentation
- Try out API endpoints directly
- Shows request/response schemas
ReDoc
- Available at /redoc
- Alternative documentation view
- Better for reading and sharing
How Documentation is Generated
FastAPI uses your:
- Function names and docstrings
- Type hints
- Pydantic models
- Path operation decorators
Example with full documentation:
1from typing import List
2from pydantic import BaseModel
3
4class Item(BaseModel):
5 name: str
6 description: str | None = None
7 price: float
8
9@app.get("/items/", response_model=List[Item], tags=["items"])
10async def get_items(skip: int = 0, limit: int = 10):
11 """
12 Retrieve items with pagination.
13
14 - **skip**: Number of items to skip
15 - **limit**: Maximum number of items to return
16 """
17 return items[skip : skip + limit]
This generates:
- Endpoint description
- Request parameters
- Response model
- Example values
- Try it out functionality
Enhancing Documentation
- Tags - Group endpoints:
1@app.get("/users/", tags=["users"])
- Summary and Description:
1@app.post("/users/",
2 summary="Create a user",
3 description="Create a new user with the provided information")
- Response Description:
1@app.get("/users/{user_id}",
2 response_description="The found user")
- Status Codes:
1@app.post("/items/", status_code=201)