Bird
Raised Fist0
FastAPIframework~5 mins

Global exception middleware in FastAPI

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction

Global exception middleware helps catch errors in one place. It stops your app from crashing and shows friendly error messages.

You want to handle all errors in your FastAPI app consistently.
You want to log errors for debugging without repeating code.
You want to send custom error responses to users.
You want to catch unexpected errors and avoid server crashes.
Syntax
FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def global_exception_middleware(request: Request, call_next):
    try:
        response = await call_next(request)
        return response
    except Exception as e:
        return JSONResponse(
            status_code=500,
            content={"detail": "An error occurred.", "error": str(e)}
        )

The middleware function must be async and accept request and call_next.

Use call_next(request) to continue processing the request.

Examples
This example catches all exceptions and returns a simple error message.
FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def catch_all_exceptions(request: Request, call_next):
    try:
        response = await call_next(request)
        return response
    except Exception:
        return JSONResponse(status_code=500, content={"message": "Oops! Something went wrong."})
This example logs the error to the console before sending a response.
FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def log_and_handle_errors(request: Request, call_next):
    try:
        response = await call_next(request)
        return response
    except Exception as e:
        print(f"Error: {e}")  # Log error
        return JSONResponse(status_code=500, content={"error": "Internal server error"})
Sample Program

This FastAPI app has a global exception middleware that catches all errors. The /divide endpoint divides two numbers. If you divide by zero, the middleware catches the error and returns a friendly JSON message instead of crashing.

FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def global_exception_middleware(request: Request, call_next):
    try:
        response = await call_next(request)
        return response
    except Exception as e:
        return JSONResponse(
            status_code=500,
            content={"detail": "An error occurred.", "error": str(e)}
        )

@app.get("/divide")
async def divide(a: int, b: int):
    result = a / b  # This can raise ZeroDivisionError
    return {"result": result}
OutputSuccess
Important Notes

Global exception middleware catches errors from all routes in one place.

It helps keep your code clean by avoiding repeated try-except blocks.

Always return a proper HTTP response to avoid hanging requests.

Summary

Global exception middleware catches and handles errors for the whole FastAPI app.

It improves user experience by sending clear error messages.

It helps developers log and debug errors easily.

Practice

(1/5)
1. What is the main purpose of global exception middleware in a FastAPI application?
easy
A. To automatically generate API documentation
B. To speed up the API response time by caching results
C. To manage database connections efficiently
D. To catch and handle errors for the entire application in one place

Solution

  1. Step 1: Understand middleware role

    Middleware runs for every request and can intercept errors globally.
  2. Step 2: Identify purpose of global exception middleware

    It catches errors from any part of the app and handles them centrally.
  3. Final Answer:

    To catch and handle errors for the entire application in one place -> Option D
  4. Quick Check:

    Global error handling = catch all errors [OK]
Hint: Global middleware handles all errors in one place [OK]
Common Mistakes:
  • Confusing middleware with caching or documentation
  • Thinking it manages database connections
  • Assuming it only handles specific routes
2. Which of the following is the correct way to add a global exception middleware in FastAPI?
easy
A. app.use_middleware(ExceptionMiddleware, handler=custom_handler)
B. app.add_middleware(ExceptionMiddleware, handler=custom_handler)
C. app.middleware(ExceptionMiddleware, handler=custom_handler)
D. app.register_middleware(ExceptionMiddleware, handler=custom_handler)

Solution

  1. Step 1: Recall FastAPI middleware syntax

    FastAPI uses add_middleware method to add middleware.
  2. Step 2: Match correct method name

    Only add_middleware is valid; others are incorrect method names.
  3. Final Answer:

    app.add_middleware(ExceptionMiddleware, handler=custom_handler) -> Option B
  4. Quick Check:

    Use add_middleware() to add middleware [OK]
Hint: Use add_middleware() method to add middleware [OK]
Common Mistakes:
  • Using incorrect method names like use_middleware or register_middleware
  • Confusing middleware with route decorators
  • Missing required parameters in add_middleware
3. Given this FastAPI middleware code snippet, what will be the response if a ValueError is raised inside a route?
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def catch_exceptions_middleware(request: Request, call_next):
    try:
        response = await call_next(request)
        return response
    except ValueError as e:
        return JSONResponse(status_code=400, content={"error": str(e)})

@app.get("/test")
async def test_route():
    raise ValueError("Invalid input")
medium
A. {"detail": "ValueError"} with status 422
B. 500 Internal Server Error with default HTML page
C. {"error": "Invalid input"} with status 400
D. Empty response with status 200

Solution

  1. Step 1: Analyze middleware error handling

    The middleware catches ValueError and returns JSONResponse with status 400 and error message.
  2. Step 2: Check route behavior

    The route raises ValueError("Invalid input"), triggering the middleware's except block.
  3. Final Answer:

    {"error": "Invalid input"} with status 400 -> Option C
  4. Quick Check:

    Middleware catches ValueError and returns JSON error [OK]
Hint: Middleware catches ValueError and returns JSON with status 400 [OK]
Common Mistakes:
  • Assuming default 500 error instead of custom JSON response
  • Confusing status codes 422 and 400
  • Ignoring middleware and expecting normal route error
4. Identify the error in this FastAPI global exception middleware code:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.middleware("http")
async def exception_middleware(request: Request, call_next):
    try:
        response = call_next(request)
        return response
    except Exception as e:
        return JSONResponse(status_code=500, content={"error": "Server error"})
medium
A. Missing await before call_next(request)
B. Incorrect exception type used
C. JSONResponse should not be returned in middleware
D. Middleware should not catch exceptions

Solution

  1. Step 1: Check async call to call_next

    call_next is an async function and must be awaited.
  2. Step 2: Identify missing await

    Code calls call_next(request) without await, causing a coroutine object to be returned instead of response.
  3. Final Answer:

    Missing await before call_next(request) -> Option A
  4. Quick Check:

    Always await async call_next() in middleware [OK]
Hint: Always await call_next(request) in async middleware [OK]
Common Mistakes:
  • Forgetting to await async call_next
  • Thinking JSONResponse can't be returned in middleware
  • Believing middleware shouldn't catch exceptions
5. You want to create a global exception middleware in FastAPI that logs all exceptions and returns a JSON error with status 500. Which code snippet correctly implements this behavior?
hard
A. @app.middleware('http') async def global_exception(request, call_next): try: return await call_next(request) except Exception as e: print(f"Error: {e}") return JSONResponse(status_code=500, content={"error": "Internal server error"})
B. app.add_middleware(ExceptionMiddleware, handler=lambda req, exc: JSONResponse({"error": str(exc)}, status_code=500))
C. @app.exception_handler(Exception) async def global_exception_handler(request, exc): return JSONResponse(status_code=500, content={"error": str(exc)})
D. app.add_exception_handler(Exception, lambda request, exc: JSONResponse({"error": "Error occurred"}, status_code=500))

Solution

  1. Step 1: Understand middleware vs exception handler

    Middleware wraps all requests and can catch exceptions globally; exception handlers are per-exception but not middleware.
  2. Step 2: Check for logging and JSON response in middleware

    @app.middleware('http') async def global_exception(request, call_next): try: return await call_next(request) except Exception as e: print(f"Error: {e}") return JSONResponse(status_code=500, content={"error": "Internal server error"}) uses @app.middleware('http') with try-except, logs error with print, and returns JSONResponse with status 500.
  3. Step 3: Verify other options

    app.add_middleware(ExceptionMiddleware, handler=lambda req, exc: JSONResponse({"error": str(exc)}, status_code=500)) uses add_middleware incorrectly; C and D are exception handlers, not middleware.
  4. Final Answer:

    @app.middleware('http') async def global_exception(request, call_next): try: return await call_next(request) except Exception as e: print(f"Error: {e}") return JSONResponse(status_code=500, content={"error": "Internal server error"}) -> Option A
  5. Quick Check:

    Middleware with try-except and logging = @app.middleware('http') async def global_exception(request, call_next): try: return await call_next(request) except Exception as e: print(f"Error: {e}") return JSONResponse(status_code=500, content={"error": "Internal server error"}) [OK]
Hint: Use @app.middleware('http') with try-except and logging [OK]
Common Mistakes:
  • Confusing middleware with exception handlers
  • Using add_middleware incorrectly for exceptions
  • Not logging exceptions inside middleware