DRF-Problems: Finally a Django library which implements RFC 7807!
TL;DR
Frustrated with tons of problems in DRF? Sorry, but this library doesn’t claim to fix them, regardless of what the library’s name may imply. :D This library does one thing only — implements RFC 7807 in our favourite Django REST Framework! Or, in layman terms, it introduces “Problem Details” in the HTTP APIs.
Why do I care?
Well, let’s suppose that you’re a star of API engineering and follow the best practices whenever building anything RESTful. Your serializers are neat, follows OpenAPI spec, incorporates proper versioning of endpoints and you know how to name your endpoints.
Things were great until you realized that you need to inform clients of different kind of errors in the same endpoint. Now you get stuck googling and going through many of Stack Overflow answers on what status code to use!
Questions, like “Should I use plain old 400? Or be more explicit and use 406 or 412? But their meanings are wrong in this context…”, pop up in your brain, but these aren’t adequately answered by Stack Overflow. And finally, you’ve hit a wall with these stupid HTTP status codes which just can’t solve your application issues (which actually makes sense, in hindsight :)) One solution you may think of is to just keep a long list of codes of all kinds of application issues, and use them in some Serializer. The grave issue with this is how these codes and their description be communicated to the API consumer. Now this will make your API difficult for any new client to use readily.
Spotify, Google, Facebook etc also have different such schemas to explain these ‘problems’. And so, the biggest drawback of REST is the lack of standards to detail the problem… until IETF published yet another document.
RFC 7807 To The Rescue
RFC 7807 provides a standard format for returning problem details from HTTP APIs. Summary of the document:
- The data model for problem details is a JSON RFC7159 object; when formatted as a JSON document, it uses the “application/problem+json” media type.
- New problem type definitions MUST document:
i. a type URI (typically, with the “http” or “https” scheme),
ii. a title that appropriately describes it (think short), and
iii. the HTTP status code for it to be used with.
Sure, it’s great. But is this implementation easy to use?
I faced this problem myself and have been looking for some solution. I stumbled upon the one for Java and its Spring library. Since I couldn’t find any similar solution for python, I realized I’d have to do it myself. So, as I am the consumer of this library too, I made this library keeping the developers in mind and how we love flexible stuff. The library is available at Github, for free (Duh! :P). It’s also indexed by Python Package Index.
It’s based around the concept of reducing your exceptions to a ‘problem detail’ model mentioned above to communicate the problem directly via the API. A view is also included in the library for the error documentation and stuff. These are well and good, but it truly stands out by extending the DRF permission concept to include exceptions in it. Moreover, it’s upto-date with the latest DRF and works well with composable permissions. Now, one would no longer be confused with which permission failing after using two permissions with OR! To sate the ‘be flexible’ desire of most developers, this library provides mixins, which works well with your existing classes.
Nice. Anyone care to tell how to try it out?
To use this library, first follow the steps you usually follow for any Django library.
- Install using pip.
pip install drf-problems
- Add ‘drf_problems’ to your INSTALLED_APPS setting in your project’s
settings.py
.
This library also requires replacing the DRF exception handler with its own implementation. In your settings.py
, update:
To use the problem description url, you need to update your urls.py
to include the urlpatterns of this library too:
With this much work, the library would work with the existing DRF exceptions and your custom ones. For the problem detail, it uses:
- type — URI made from either
code
ordefault_code
attribute of exception. - title — either
title
ordefault_detail
attribute of exception. - description (used in error documentation) — uses
description
attribute of exception.
To use the error documentation with custom exceptions, these need to be registered so that its code can be inferred. Here’s a way to register your exception with a decorator:
Now with this done and using the ‘invalid_version’ code for the error documentation endpoint, its description and status code can be obtained.
To use these kinds of exceptions with DRF permissions, you would first need to ensure that the corresponding view inherits from the provided drf_problems.mixins.AllowPermissionWithExceptionViewMixin
mixin, like below:
In the example above, a working permission is used which this library provides. MinimumVersionRequiredPermission
takes in minimum API version as initialisation argument and checks that the request version is more than or equal to this minimum version. If not, then drf_problems.exceptions.DeprecatedVersionUsedException
is raised.
Finally, for the permissions, use either drf_problems.permissions.ProblemPermissionMixin
mixin class with your existing permissions, or extend directly from drf_problems.permissions.BaseProblemPermission
. The exception instance to raise is used from the exception
attribute defined on the permission or created from the class in exception_class
attribute defined on the permission.
Here’s a sample permissions class:
What if it blows up my application?
While I’ve released V1.0.0, I cannot take guarantee of any bugs which may result in your application after including this library. Just open up an issue here, and we can figure out what can be done to solve it.
I appreciate all contributions — whether raising an issue, giving ideas for enhancement, opening a pull request or just discussing another IETF document! :D
Didn’t quite catch your name yet
Well, sorry about introducing myself so late. I’m Shivansh Saini, an undergrad student of CSE in IIT (BHU), Varanasi. I’ve had some experience in developing and deploying web applications, whether it be on Dynos, App Engine, EC2 or Kubernetes architecture with Ingress. Besides the obvious backend development, I’ve done frontend projects like a PWA using Vue.JS and android apps. It’s my first time writing a technical blog, so I’d love to hear any criticisms you have for me on anything I can improve on or did well.
Signing off,
TheFaker