drf-standardized-errors icon indicating copy to clipboard operation
drf-standardized-errors copied to clipboard

Add support for adding 'extra details' (meta) in error response

Open sachingaikwad123 opened this issue 9 months ago • 4 comments

Add support for adding 'extra details' (meta) in error response

Example: We have a REST API 'add-employee'. It adds employee with given information into Django Database. It checks if employee already exists with "mobile number" and returns error: "Employee already exists".

When returning "Employee already exists" error, we also want to return "Existing Employee Details" to client in this error response. This can be useful for client (frontend) to show it to user.

Can we add support to return some "extra_details" or "meta" information along with error response?

If we already support this, then please let me know how to do this. I was not able to figure this out from docs/my experiments.

sachingaikwad123 avatar Feb 13 '25 12:02 sachingaikwad123

@sachingaikwad123, this is already possible as long as this is not a validation error raised in a serializer.

First, define your custom exception

from rest_framework.exceptions import APIException
class ObjectAlreadyExistsError(APIException):
    status_code = 409
    default_detail = 'Object already exists'
    default_code = 'conflict'

    def __init__(self, detail=None, code=None, extra_data=None):
        self.extra_data = extra_data
        super().__init__(detail, code)

create a custom exception formatter and update your settings to point to the new exception formatter

from drf_standardized_errors.formatter import ExceptionFormatter
class CustomExceptionFormatter(ExceptionFormatter):
    def format_error_response(self, error_response: ErrorResponse):
        resp = super().format_error_response(error_response)
        if isinstance(self.exc, ObjectAlreadyExistsError):
            resp["extra_data"] = self.exc.extra_data
        return resp

raise the exception in your code

class EmployeeCreateView(CreateAPIView):
    ...
    def create(self, request, *args, **kwargs):
        ...
        if some_condition:
            raise ObjectAlreadyExistsError("Employee Already Exists", extra_data={"id":1, "name": "John Doe"})
        ...

Then the API response should look like

{
  "type": "client_error",
  "errors": [
    {
      "code": "conflict",
      "detail": "Employee Already Exists",
      "attr": null
    }
  ],
  "extra_data": {"id": 1, "name": "John Doe"}
}

Note that attaching extra data to ValidationErrors or its subclasses raised in a serializer doesn't work because DRF creates new ValidationError instances when they are raised. See here and here

ghazi-git avatar Feb 16 '25 21:02 ghazi-git

Excellent. I could not figure this out myself. I was raising ValidationError manually and was running into not expected 'errors' dict in response.

Thanks for your detailed response. I will try this out with a custom exception class.

sachingaikwad123 avatar Feb 17 '25 04:02 sachingaikwad123

It would be nice to have it in the library somehow... For instance agree on some 'attribute' which will be propagated as metadata. Current suggestion is amazing, but the problem - how to add this metadata to validation errors and all other errors?

sshishov avatar May 08 '25 06:05 sshishov

To add metadata to validation-errors-raised-outside-the-serializer and other errors, a similar approach should work

class EmployeeCreateView(CreateAPIView):
    ...
    def create(self, request, *args, **kwargs):
        ...
        if some_condition:
            exc = ValidationError("Employee Already Exists")
            exc.metadata = {"id":1, "name": "John Doe"}
            raise exc
        ...

then in the formatter class, get the data from the metadata attribute

from drf_standardized_errors.formatter import ExceptionFormatter
class CustomExceptionFormatter(ExceptionFormatter):
    def format_error_response(self, error_response: ErrorResponse):
        resp = super().format_error_response(error_response)
        resp["metadata"] = getattr(self.exc, "metadata", None)
        return resp

For validation errors raised inside a serializer, you cannot pass metadata from the serializer up to the exception handler due to how drf deals with those specific errors. What you can do instead is either raise another type of exception in the serializer or move the validation outside of the serializer (to the view for example.)

ghazi-git avatar Jun 02 '25 13:06 ghazi-git