Skip to content

A library for Django Rest Framework returning consistent, predictable and easy-to-parse API error messages inspired by RFC7807 guidelines.

License

Notifications You must be signed in to change notification settings

gripep/drf-simple-api-errors

Repository files navigation

Django Rest Framework Simple API Errors

PyPI test workflow codecov pyversions

What is this?

A library for Django Rest Framework returning consistent, predictable and easy-to-parse API error messages.

This library was built with RFC7807 guidelines in mind, but with a small twist: it defines a "problem detail" as a list, but it still serves as a way to include errors in a predictable and easy-to-parse format for any API consumer. Error messages are formatted using RFC7807 keywords and DRF exception data.

What's different?

Compared to other similar and popular libraries, this library:

  • Is based on RFC7807 guidelines
  • Aims to provide not only a standardized format for error details, but also human-readable error messages (perfect for both internal and public APIs)
  • Transforms both django.core.exceptions.ValidationError and rest_framework.errors.ValidationError to API errors, so you don't have to handle error raised by services/domain logic, clean(), or other functions/methods

Table of Contents

Installation

Install using the command line:

pip install drf-simple-api-errors

Usage

Exception Handler

Add EXCEPTION_HANDLER in your REST_FRAMEWORK settings of your Django project settings file:

REST_FRAMEWORK = {
    # ...
    "EXCEPTION_HANDLER": "drf_simple_api_errors.exception_handler",
}

Error structure overview

API error messages typically include the following keys:

  • "title" (str): A brief summary that describes the problem type
  • "detail" (list[str] | None): A list of specific explanations related to the problem
  • "invalid_params" (list[dict] | None): A list of dict containing details about parameters that were invalid or malformed in the request. Each dict within this list provides:
    • "name" (str): The name of the parameter that was found to be invalid
    • "reasons" (list[str]): A list of strings describing the specific reasons why the parameter was considered invalid or malformed
{
    "title": "Error message.",
    "detail": [
        "error",
        ...
    ],
    "invalid_params": [
        {
            "name": "field_name",
            "reason": [
                "error",
                ...
            ]
        },
        ...
    ]
}

Example JSON Error Responses

Field validation errors

{
    "title": "Error message.",
    "invalid_params": [
        {
            "name": "field_name",
            "reason": [
                "error",
                ...
            ]
        },
        ...
    ]
}

Non-fields validation errors

{
  "title": "Error message.",
  "detail": [
    "error",
    ...
  ]
}

Other bad requests with no detail

{
  "title": "Error message."
}

Settings

Default available settings:

DRF_SIMPLE_API_ERRORS = {
    "CAMELIZE": False,
    "EXTRA_HANDLERS": [],
    "FIELDS_SEPARATOR": ".",
}
  • CAMELIZE

Camel case support for Django Rest Framework exceptions JSON error responses.

If CAMELIZE is set to True:

{
  "title": "Error message.",
  "invalidParams": [
    {
      "name": "fieldName",
      "reason": [
        "error",
        ...
      ]
    }
    ...
  ]
}
  • EXTRA_HANDLERS

Support for exceptions that differ from the standard structure of the Django Rest Framework.

For instance, you may want to specify you own exception:

class AuthenticationFailed(exceptions.AuthenticationFailed):
    def __init__(self, detail=None, code=None):
        """
        Builds a detail dictionary for the error to give more information
        to API users.
        """
        detail_dict = {"detail": self.default_detail, "code": self.default_code}

        if isinstance(detail, dict):
            detail_dict.update(detail)
        elif detail is not None:
            detail_dict["detail"] = detail

        if code is not None:
            detail_dict["code"] = code

        super().__init__(detail_dict)

Use exception in code:

def my_func():
    raise AuthenticationFailed(
        {
            "detail": _("Error message."),
            "messages": [
                {
                    "metadata": "metadata_data",
                    "type": "type_name",
                    "message": "error message",
                }
            ],
        }
    )

This will result in:

AuthenticationFailed(
    {
        "detail": "Error message.",
        "messages": [
            {
                "metadata": "metadata_data",
                "type": "type_name",
                "message": "error message",
            }
        ],
    }
)

You can handle this by creating a handlers.py file and specifying an handler for your use case:

def handle_exc_custom_authentication_failed(exc):
    from path.to.my.exceptions import AuthenticationFailed

    if isinstance(exc, AuthenticationFailed):
        try:
            exc.detail = exc.detail["messages"][0]["message"]
        except (KeyError, IndexError):
            exc.detail = exc.detail["detail"]

    return exc

Then add it to the EXTRA_HANDLERS list in this package settings:

DRF_SIMPLE_API_ERRORS = {
    "EXTRA_HANDLERS": [
        "path.to.my.handlers.handle_exc_custom_authentication_failed",
        # ...
    ]
}
  • FIELDS_SEPARATOR

Support for nested dicts containing multiple fields to be flattened.

If FIELDS_SEPARATOR is set to .:

{
    "field1": {
        "field2": "value"
    }
}

Will result in:

{
    "field1.field2": "value"
}

Testing

All the necessary commands are included in the Makefile.

We are using tox and poetry to run tests in every supported Python version.

Run test with the commands below:

make install
make test

Support

Please open an issue.

Contributing

Please use the Github Flow. In a nutshell, create a branch, commit your code, and open a pull request.

About

A library for Django Rest Framework returning consistent, predictable and easy-to-parse API error messages inspired by RFC7807 guidelines.

Topics

Resources

License

Stars

Watchers

Forks