Initial commit
This commit is contained in:
115
skills/api-design-standards/templates/error-handler.py
Normal file
115
skills/api-design-standards/templates/error-handler.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# Grey Haven Studio - Error Handler Template
|
||||
# Add this to app/core/exceptions.py
|
||||
|
||||
from fastapi import HTTPException, Request, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from sqlalchemy.exc import IntegrityError, OperationalError
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
|
||||
"""Handle HTTPException with standard error format."""
|
||||
logger.warning(f"HTTP exception: {exc.status_code} - {exc.detail}")
|
||||
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={
|
||||
"error": exc.detail,
|
||||
"status_code": exc.status_code,
|
||||
},
|
||||
headers=exc.headers,
|
||||
)
|
||||
|
||||
|
||||
async def validation_exception_handler(
|
||||
request: Request, exc: RequestValidationError
|
||||
) -> JSONResponse:
|
||||
"""Handle Pydantic validation errors."""
|
||||
errors = []
|
||||
for error in exc.errors():
|
||||
field_path = ".".join(str(loc) for loc in error["loc"] if loc != "body")
|
||||
errors.append(
|
||||
{
|
||||
"field": field_path if field_path else None,
|
||||
"message": error["msg"],
|
||||
"code": error["type"],
|
||||
}
|
||||
)
|
||||
|
||||
logger.warning(f"Validation error: {errors}")
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content={
|
||||
"error": "Validation error",
|
||||
"detail": errors,
|
||||
"status_code": 422,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def integrity_error_handler(
|
||||
request: Request, exc: IntegrityError
|
||||
) -> JSONResponse:
|
||||
"""Handle database integrity errors."""
|
||||
logger.error(f"Integrity error: {exc}")
|
||||
|
||||
error_detail = "A resource with this unique value already exists"
|
||||
if "foreign key" in str(exc).lower():
|
||||
error_detail = "Referenced resource does not exist"
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
content={
|
||||
"error": error_detail,
|
||||
"status_code": 409,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def operational_error_handler(
|
||||
request: Request, exc: OperationalError
|
||||
) -> JSONResponse:
|
||||
"""Handle database operational errors."""
|
||||
logger.error(f"Database operational error: {exc}")
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
content={
|
||||
"error": "Service temporarily unavailable",
|
||||
"detail": "Database connection error. Please try again later.",
|
||||
"status_code": 503,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
||||
"""Catch-all handler for unexpected exceptions."""
|
||||
logger.exception(f"Unhandled exception: {exc}")
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
content={
|
||||
"error": "Internal server error",
|
||||
"status_code": 500,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Register in main.py:
|
||||
# from app.core.exceptions import (
|
||||
# http_exception_handler,
|
||||
# validation_exception_handler,
|
||||
# integrity_error_handler,
|
||||
# operational_error_handler,
|
||||
# generic_exception_handler,
|
||||
# )
|
||||
#
|
||||
# app.add_exception_handler(HTTPException, http_exception_handler)
|
||||
# app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||
# app.add_exception_handler(IntegrityError, integrity_error_handler)
|
||||
# app.add_exception_handler(OperationalError, operational_error_handler)
|
||||
# app.add_exception_handler(Exception, generic_exception_handler)
|
||||
Reference in New Issue
Block a user