FastAPI: From Basics to Production · Building Real APIs

Consistent Error Handling

Return predictable error responses with HTTPException and custom handlers.

Raise HTTPException for expected HTTP errors. For domain errors, raise your own exceptions in services (e.g., NotFound, Conflict) and map them to responses with @app.exception_handler, so services stay HTTP-agnostic. Customize the 422 validation response if clients expect a specific format, and add a catch-all handler that logs unexpected errors with a request ID and returns a generic 500.

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


class DomainError(Exception):
    status_code = 400
    code = "domain_error"


class NotFound(DomainError):
    status_code = 404
    code = "not_found"


class Conflict(DomainError):
    status_code = 409
    code = "conflict"


@app.exception_handler(DomainError)
async def domain_error_handler(request: Request, exc: DomainError) -> JSONResponse:
    return JSONResponse(status_code=exc.status_code, content={"code": exc.code, "detail": str(exc)})


@app.exception_handler(Exception)
async def unhandled(request: Request, exc: Exception) -> JSONResponse:
    logger.exception("unhandled error", extra={"path": request.url.path})
    return JSONResponse(status_code=500, content={"code": "internal_error", "detail": "Something went wrong"})

Common pitfalls

  • Leaking stack traces or SQL errors in responses.
  • Services raising HTTPException (couples domain to HTTP).
  • Inconsistent error shapes between endpoints.

Key points

  • Domain exceptions + handlers
  • Consistent error envelope
  • Log unexpected errors; return generic 500s

Further reading: FastAPI: Handling errors

Go deeper: FastAPI and Starlette: Middleware, Exceptions and Dependencies in the Request Lifecycle course traces this step of the request, with a production bug and a runnable simulation.

Practice questions

Why map domain exceptions to HTTP in handlers instead of raising HTTPException in services?

Answer

Services stay reusable outside HTTP (jobs, CLI), and the HTTP mapping is centralized and consistent.

What status does FastAPI return for request validation errors by default?

Answer

422 Unprocessable Entity, with a detail list describing each invalid field's location and message.

Open in the app to mark lessons complete, take notes and track your progress.