đŚ FastAPI Error Handling: Your Appâs Safety Net
Imagine youâre a traffic controller at a busy intersection. When cars follow the rules, everything flows smoothly. But when something goes wrongâa car runs a red light, takes a wrong turn, or breaks downâyou need to stop traffic, signal the problem, and guide everyone safely. Thatâs exactly what error handling does in FastAPI!
đŻ What Youâll Learn
Weâre going to explore three superpowers that help your FastAPI app handle problems gracefully:
- HTTPException â The âstop signâ that tells users something went wrong
- Custom Exception Handlers â Your personal traffic controller for specific problems
- Validation Errors â The gatekeeper that checks if data looks right before letting it in
đ HTTPException: The Stop Sign
What Is It?
Think of HTTPException as a big red STOP sign. When your app canât do what someone asked, it raises this exception to say: âHey! Somethingâs not right here!â
Real Life Example
Imagine a library app. A user asks for Book #999, but that book doesnât exist.
from fastapi import FastAPI, HTTPException
app = FastAPI()
books = {1: "Harry Potter", 2: "Lord of Rings"}
@app.get("/books/{book_id}")
def get_book(book_id: int):
if book_id not in books:
raise HTTPException(
status_code=404,
detail="Book not found!"
)
return {"book": books[book_id]}
What Happens?
- User asks for
/books/999 - Book #999 doesnât exist
- App raises HTTPException with code
404 - User sees:
{"detail": "Book not found!"}
Common Status Codes (Like Traffic Signs!)
| Code | Meaning | When to Use |
|---|---|---|
| 400 | Bad Request | User sent broken data |
| 401 | Unauthorized | User needs to log in |
| 403 | Forbidden | User canât access this |
| 404 | Not Found | Thing doesnât exist |
| 500 | Server Error | Something broke inside |
Adding Extra Information
You can add headers to give more details:
raise HTTPException(
status_code=401,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"}
)
đ¨ Custom Exception Handlers: Your Personal Traffic Controller
What Is It?
Sometimes the default stop sign isnât enough. You want to handle specific problems in your own special way. Custom Exception Handlers let you create your own traffic controllers!
The Story
Imagine your app has a special rule: Users can only ask for 3 things per minute. When they ask for more, you want to give a friendly messageânot a scary error.
Step 1: Create Your Own Exception
class TooManyRequestsError(Exception):
def __init__(self, wait_time: int):
self.wait_time = wait_time
Step 2: Create the Handler
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(TooManyRequestsError)
async def too_many_handler(
request: Request,
exc: TooManyRequestsError
):
return JSONResponse(
status_code=429,
content={
"message": "Slow down, friend!",
"wait_seconds": exc.wait_time,
"tip": "Grab a coffee â"
}
)
Step 3: Use It!
@app.get("/data")
def get_data():
# If user made too many requests...
raise TooManyRequestsError(wait_time=30)
What User Sees
{
"message": "Slow down, friend!",
"wait_seconds": 30,
"tip": "Grab a coffee â"
}
Much nicer than a scary error message!
Flow Diagram
graph TD A[User Request] --> B{Check Rules} B -->|OK| C[Return Data] B -->|Too Many| D[Raise TooManyRequestsError] D --> E[Custom Handler Catches It] E --> F[Friendly JSON Response]
â Validation Errors: The Gatekeeper
What Is It?
Validation Errors happen when someone sends data that doesnât match what you expected. Itâs like a gatekeeper checking IDs at the door!
The Story
Your app expects a userâs age. Someone types âtwentyâ instead of â20â. The gatekeeper says: âSorry, thatâs not a number!â
How FastAPI Validates
FastAPI uses Pydantic to check data automatically:
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
email: str
@app.post("/users")
def create_user(user: User):
return {"created": user.name}
What Happens With Bad Data?
If someone sends:
{"name": "Alex", "age": "twenty", "email": "alex@mail"}
FastAPI automatically responds:
{
"detail": [
{
"loc": ["body", "age"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
Making Validation Errors Prettier
You can customize how validation errors look:
from fastapi.exceptions import RequestValidationError
@app.exception_handler(RequestValidationError)
async def validation_handler(
request: Request,
exc: RequestValidationError
):
errors = []
for error in exc.errors():
field = error["loc"][-1]
errors.append({
"field": field,
"problem": error["msg"]
})
return JSONResponse(
status_code=422,
content={
"message": "Oops! Check your data",
"errors": errors
}
)
Now User Sees
{
"message": "Oops! Check your data",
"errors": [
{"field": "age", "problem": "value is not a valid integer"}
]
}
Common Validation Types
| Check | Example | What It Does |
|---|---|---|
int |
age: int |
Must be a number |
str |
name: str |
Must be text |
EmailStr |
email: EmailStr |
Must look like email |
gt=0 |
Field(gt=0) |
Must be greater than 0 |
max_length |
Field(max_length=50) |
Canât be too long |
đ Putting It All Together
Hereâs a complete mini-app showing all three concepts:
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from pydantic import BaseModel, Field
app = FastAPI()
# 1. Custom Exception
class RateLimitError(Exception):
def __init__(self, seconds: int):
self.seconds = seconds
# 2. Custom Handler for RateLimitError
@app.exception_handler(RateLimitError)
async def rate_limit_handler(req: Request, exc: RateLimitError):
return JSONResponse(
status_code=429,
content={"wait": exc.seconds}
)
# 3. Custom Handler for ValidationError
@app.exception_handler(RequestValidationError)
async def validation_handler(req: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content={"errors": "Check your input!"}
)
# 4. Data Model with Validation
class Item(BaseModel):
name: str = Field(min_length=1)
price: float = Field(gt=0)
items_db = {1: "Apple", 2: "Banana"}
@app.get("/items/{item_id}")
def get_item(item_id: int):
# HTTPException example
if item_id not in items_db:
raise HTTPException(
status_code=404,
detail="Item not found"
)
return {"item": items_db[item_id]}
@app.post("/items")
def create_item(item: Item):
# Validation happens automatically!
return {"created": item.name}
đŻ Quick Summary
| Concept | What It Does | When to Use |
|---|---|---|
| HTTPException | Stops and returns error | Item not found, access denied |
| Custom Handlers | Your own error responses | Special business rules |
| Validation Errors | Checks incoming data | Wrong data types, missing fields |
đĄ Pro Tips
- Always be friendly â Users should understand what went wrong
- Use the right status code â 404 for ânot foundâ, 400 for âbad requestâ
- Donât expose secrets â Never show database errors to users
- Log errors â Save detailed errors for yourself, show simple ones to users
đ You Did It!
You now know how to:
- â Stop bad requests with HTTPException
- â Create custom responses with Exception Handlers
- â Automatically check data with Validation Errors
Your FastAPI app is now a well-controlled intersection where everything flows smoothlyâand when problems happen, everyone knows exactly what to do!
Happy coding! đ