Exceptions¶
The core.exceptions module defines a centralized exception handling strategy for the API.
It ensures that all errors returned by the backend follow a consistent, structured format, regardless of where they originate.
Purpose¶
The exception layer is responsible for:
- standardizing API error responses
- normalizing DRF and validation errors
- preserving compatibility with existing DRF behavior
- simplifying frontend error handling
Entry Point¶
The main function:
api_exception_handler(exc, context)
This function is registered in Django settings:
REST_FRAMEWORK = { "EXCEPTION_HANDLER": "core.exceptions.api_exception_handler" }
Response Format¶
All errors are wrapped in a consistent structure:
{ "error": { "status_code": 400, "detail": "Validation error", "fields": { "field_name": ["Error message"] } }, "detail": "Validation error", "field_name": ["Error message"] }
Structure Explained¶
error (object)¶
Contains the structured error metadata:
- status_code → HTTP status code
- detail → human-readable message
- fields → field-level validation errors (if any)
detail (top-level)¶
- duplicated for DRF compatibility
- allows tests and clients to use standard DRF patterns
field errors (top-level)¶
- flattened into the root response
- enables simple frontend usage
Example:
{ "detail": "Validation error", "email": ["This field is required."] }
Behavior¶
Case 1 — Standard DRF error¶
Input:
{ "email": ["This field is required."] }
Output:
- detail = "Validation error"
- fields populated
Case 2 — DRF error with detail¶
Input:
{ "detail": "Not found" }
Output:
- detail preserved
- fields empty
Case 3 — Mixed errors¶
Input:
{ "detail": "Invalid input", "email": ["Invalid email"] }
Output:
- detail extracted
- fields separated
Case 4 — Non-dict error¶
Input:
"Something went wrong"
Output:
- treated as detail string
Design Decisions¶
1. Keep DRF compatibility¶
detailremains available at top-level- tests and existing clients continue to work
2. Add structured envelope¶
errorobject enables:- better frontend parsing
- logging consistency
- future extensibility
3. Flatten field errors¶
- avoids deep nesting in frontend
- simplifies form handling
Responsibilities¶
Exception Handler¶
- formats response
- extracts detail and field errors
- builds consistent payload
Views / Services¶
- raise exceptions
- do not format responses
What Should Raise Exceptions?¶
Services¶
- domain errors
- validation failures
- permission issues
Example:
raise MembershipValidationError("Invalid role")
DRF¶
- serializer validation
- permission checks
- not found errors
What Should NOT Happen¶
Avoid:
- returning Response(...) inside services
- formatting errors in views
- inconsistent error shapes
Benefits¶
This approach provides:
- consistent API responses
- easier frontend integration
- better debugging and logging
- centralized error handling
Example¶
Input (serializer error)¶
{ "email": ["This field is required."] }
Output¶
{ "error": { "status_code": 400, "detail": "Validation error", "fields": { "email": ["This field is required."] } }, "detail": "Validation error", "email": ["This field is required."] }
Future Improvements¶
Possible extensions:
- error codes (e.g. "INVALID_ROLE")
- error categories (validation, permission, system)
- localization (multi-language errors)
- correlation IDs for tracing
Summary¶
The exception layer ensures that:
- all API errors are consistent
- frontend integration is simple
- DRF compatibility is preserved
It acts as the single source of truth for error formatting in the backend.