drf-exceptions-hog

Standardizes Django REST Framework error responses into a predictable, Stripe-style JSON format for easier frontend parsing.

Library
PyPI
v0.4.0
67stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
47/100Fair
Development Activity44
Maintenance28
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture80
Code Quality82
Innovation72
Learning Curve60

drf-exceptions-hog is a small Django REST Framework plugin, built and maintained by PostHog, that normalizes every API error response into one consistent JSON shape: type, code, detail, attr, and optionally list. Instead of DRF’s default behavior, where different exception classes produce differently shaped payloads, this package routes everything through a single exception handler so frontend clients can parse errors with one code path regardless of language or framework.

It installs as a drop-in replacement for DRF’s EXCEPTION_HANDLER setting, requires no changes to existing views or serializers, and supports optional configuration for custom error-reporting hooks (e.g. Sentry), nested validation error keys, and returning multiple exceptions in a single response for form-heavy APIs.

What You Get

  • A single exception_handler function you wire into DRF’s EXCEPTION_HANDLER setting
  • Consistent type/code/detail/attr fields on every error response, including nested serializer validation errors
  • Optional SUPPORT_MULTIPLE_EXCEPTIONS mode that returns all validation errors in one response instead of just the first
  • A pluggable EXCEPTION_REPORTING hook for wiring errors into Sentry, New Relic, or other APM/error-tracking tools
  • Built-in handling for Django’s Http404, PermissionDenied, and ProtectedError alongside standard DRF exception types

Common Use Cases

  • Standardizing error responses across a DRF API so a single frontend parser can handle all error types
  • Returning every validation failure on a form submission in one response instead of forcing repeated round-trips
  • Piping unhandled exceptions into an error-tracking service via the EXCEPTION_REPORTING hook
  • Localizing user-facing error messages via Django’s translation framework without changing error-handling logic

Under The Hood

Architecture drf-exceptions-hog is architected as a single DRF exception-handler function (exceptions_hog/handler.py: exception_handler) that DRF calls in place of its default handler via the EXCEPTION_HANDLER setting. The handler first normalizes Django-native exceptions (Http404, PermissionDenied, ProtectedError) into DRF equivalents, then extracts a normalized code/key pair per exception via _get_main_exception_and_code and _normalize_exception_codes, a recursive walker that flattens nested serializer validation dicts into flat parsed_keys/exception_code pairs. Configuration lives in exceptions_hog/settings.py, a thin wrapper around DRF’s own APISettings pattern, which supports import-string settings like EXCEPTION_REPORTING. A small exceptions.py module defines a mixin (ExceptionClass) plus one concrete exception (ProtectedObjectException) that bridges Django’s ProtectedError into DRF’s exception model. There’s no persistent state, no models, and no views, so the entire library is one call path invoked per exception, keeping the blast radius of any change limited to that single handler function.

Tech Stack The package targets Django REST Framework (djangorestframework>=3.9.4 per setup.py) atop Django 3.1/3.2, and is otherwise dependency-free, using only Django’s and DRF’s own APIs (rest_framework.exceptions, rest_framework.views.set_rollback, rest_framework.settings.APISettings) rather than any external libraries. Packaging is plain setuptools (setup.py, setup.cfg), with no build step or compiled assets. The test suite runs under pytest and pytest-django, with a small test_project/ Django project supplying settings and URLconf for integration-style tests, and CI (runtests.py, .github/workflows/CI.yml) matrixes across Python 3.7-3.10 and Django 3.1.x/3.2.x. Local dev tooling is flake8, black, and isort, plus mypy, all invoked from a single runtests.py wrapper forked from DRF’s own test runner.

Code Quality Testing is thorough for the package’s size: tests/test_handler.py (583 lines) unit-tests the handler’s internal helpers directly across dozens of exception shapes, while tests/test_api.py (196 lines) exercises the handler through a real DRF view, covering multiple-exception mode, nested serializer errors, and Http404/PermissionDenied/ProtectedError translation. Type hints are used throughout the source and checked via mypy in CI; formatting and import order are enforced via black and isort, with flake8 for linting, all run as required CI gates rather than local suggestions. Error handling is intentional rather than defensive: unhandled exception shapes fall back to a generic server_error type and a translated default detail message rather than leaking internals, a security consideration called out explicitly in the README.

API Design The public API is deliberately minimal: a single exception_handler function to point EXCEPTION_HANDLER at, plus a settings dict (EXCEPTIONS_HOG) with four options, EXCEPTION_REPORTING, ENABLE_IN_DEBUG, NESTED_KEY_SEPARATOR, and SUPPORT_MULTIPLE_EXCEPTIONS, with no new classes to subclass and no changes required to existing views or serializers, making adoption a one-line settings change. Custom error types and codes are supported by setting exception_type or default_type attributes on exception instances rather than requiring a registry, keeping the extension point simple. The response schema itself is explicitly documented in the README with worked JSON examples for both single and multiple-exception cases, and is modeled on Stripe’s API error format, a purposeful, well-precedented design choice rather than a novel invention.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search