Ojasa Mirai

Ojasa Mirai

FastAPI

Loading...

Learning Level

🟢 Beginner🔵 Advanced
🚀 Error Basics📚 HTTP Exceptions📚 Custom Exceptions📚 Error Responses📚 Exception Handlers📚 Validation Errors📚 Error Logging📚 Error Best Practices
Fastapi/Error Handling/Exception Handlers

Exception Handlers

Learn the fundamentals of exception handlers in FastAPI.

🎯 Core Concept

Exception handlers allow you to customize how your API responds when errors occur. Instead of returning raw Python exceptions, handlers convert exceptions into properly formatted HTTP responses that clients can understand and process.

📖 What You'll Learn

In this section, you'll understand:

  • How to register exception handlers
  • How it applies to real-world API development
  • Handling multiple exception types
  • Common patterns and best practices

💡 Basic Exception Handler

from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

class ItemNotFoundError(Exception):
    def __init__(self, item_id: int):
        self.item_id = item_id

@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request, exc):
    return JSONResponse(
        status_code=404,
        content={
            "error": "ItemNotFound",
            "message": f"Item {exc.item_id} not found"
        }
    )

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id > 100:
        raise ItemNotFoundError(item_id)
    return {"id": item_id}

🔍 Multiple Exception Handlers

from fastapi import FastAPI
from fastapi.responses import JSONResponse
import logging

app = FastAPI()
logger = logging.getLogger(__name__)

class DatabaseError(Exception):
    pass

class ValidationError(Exception):
    def __init__(self, field: str, message: str):
        self.field = field
        self.message = message

@app.exception_handler(DatabaseError)
async def db_error_handler(request, exc):
    logger.error(f"Database error: {exc}")
    return JSONResponse(
        status_code=500,
        content={
            "error": "DatabaseError",
            "message": "Database operation failed"
        }
    )

@app.exception_handler(ValidationError)
async def validation_error_handler(request, exc):
    return JSONResponse(
        status_code=422,
        content={
            "error": "ValidationError",
            "field": exc.field,
            "message": exc.message
        }
    )

@app.post("/users/")
async def create_user(user: dict):
    if not user.get("email"):
        raise ValidationError("email", "Email is required")

    try:
        # Save to database
        db.save_user(user)
    except Exception as e:
        raise DatabaseError("Failed to save user")

    return user

💼 Production Exception Handler

import uuid
from datetime import datetime
from typing import Optional

class AppException(Exception):
    def __init__(self, code: str, message: str, status_code: int = 400):
        self.code = code
        self.message = message
        self.status_code = status_code

@app.exception_handler(AppException)
async def app_exception_handler(request, exc):
    request_id = str(uuid.uuid4())

    logger.warning(
        f"AppException: {exc.code} | Message: {exc.message} | Request ID: {request_id}"
    )

    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": {
                "code": exc.code,
                "message": exc.message,
                "request_id": request_id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
    )

@app.exception_handler(Exception)
async def general_exception_handler(request, exc):
    request_id = str(uuid.uuid4())

    logger.error(
        f"Unhandled exception | Error: {str(exc)} | Request ID: {request_id}",
        exc_info=True
    )

    return JSONResponse(
        status_code=500,
        content={
            "error": {
                "code": "INTERNAL_ERROR",
                "message": "An unexpected error occurred",
                "request_id": request_id
            }
        }
    )

How it applies to real-world API development

Exception handlers are essential for:

  • **Centralized error handling**: Handle errors consistently across endpoints
  • **Logging**: Log all errors for monitoring and debugging
  • **Client feedback**: Return user-friendly messages instead of stack traces
  • **Status codes**: Ensure correct HTTP status codes for different error types

Example - Microservices with error handling:

class ServiceUnavailableError(AppException):
    def __init__(self, service_name: str):
        super().__init__(
            code="SERVICE_UNAVAILABLE",
            message=f"{service_name} service is temporarily unavailable",
            status_code=503
        )

class RateLimitError(AppException):
    def __init__(self, retry_after: int):
        super().__init__(
            code="RATE_LIMITED",
            message="Too many requests. Please try again later",
            status_code=429
        )
        self.retry_after = retry_after

@app.exception_handler(RateLimitError)
async def rate_limit_handler(request, exc):
    return JSONResponse(
        status_code=429,
        headers={"Retry-After": str(exc.retry_after)},
        content={
            "error": {
                "code": exc.code,
                "message": exc.message,
                "retry_after": exc.retry_after
            }
        }
    )

@app.get("/payment-status/{transaction_id}")
async def get_payment_status(transaction_id: str):
    try:
        return payment_service.get_status(transaction_id)
    except payment_service.ConnectionError as e:
        raise ServiceUnavailableError("PaymentService")

Common patterns and best practices

  • ✅ Create custom exception classes for different error types
  • ✅ Register handlers for specific exceptions
  • ✅ Log exceptions for debugging and monitoring
  • ✅ Include request IDs for traceability
  • ✅ Return appropriate HTTP status codes
  • ✅ Don't expose internal error details in production
  • ✅ Handle exceptions hierarchically (specific to general)
  • ✅ Test exception handlers with various error scenarios

🔑 Key Takeaways

  • ✅ Understand the purpose of exception handlers
  • ✅ Know when to apply this pattern
  • ✅ Recognize its benefits in real-world scenarios
  • ✅ Be prepared to use it in your projects

Ready to explore more? Check out the advanced section for production patterns and edge cases.


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn