Django: When REST May Not Be Enough and How a GraphQL Layer Can Help Save You

In this article, we’ll present a common problem faced by maintainers of a mature REST-based API which has been built using the Django REST Framework. We’ll then proceed to demonstrate how GraphQL can provide a solution to this problem and how this solution can be applied by adding just two lines of code to your Django project via use of the GraphWrap library.

The Set-Up

P. Walnuts & Co. are a well established publishing company. They have a mature REST based API which is built using the Django REST Framework (DRF). This API, among other things, allows a consumer to fetch information about books or authors . This single API has three primary consumers: the P. Walnuts & Co website, the P. Walnuts & Co Android/OS App and external clients.

class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['name', 'active', 'profile']
class BookSerializer(serializers.ModelSerializer):
author = serializers.HyperlinkedRelatedField(
view_name='author-detail', read_only=True)
class Meta:
model = Book
fields = ['author', 'title', 'page_count']

The Problem

The company website includes a page where user’s can browse the books available. When a book is clicked on, we redirect to a page which gives some more information about the book and its author. To fetch the data to serve this page, the web client calls, for example /book/1/ , which would give a response which looks like this:

{
'author': '/author/1/',
'title': 'Lovely Gardens',
'page_count': 200,
}

Attempting a Solution in Native DRF

The obvious solution to satisfy our web client here is to simply expose more author fields on our /book endpoint. We’ll add an author_full field to the BookSerializerlike so:

class BookSerializer(serializers.ModelSerializer):
author = serializers.HyperlinkedRelatedField(
view_name='author-detail', read_only=True)
author_full = AuthorSerializer(source='author_full')
class Meta:
model = Book
fields = ['author', 'author_full', 'title', 'page_count']
{
'author': '/author/1/',
'title': 'Lovely Gardens',
'page_count': 200,
'author_full': {
'name': 'Emilie B.',
'active': True,
'profile': '/profile/1/',
}
}

How GraphQL Can Help Us

Enter GraphQL: GraphQL is designed so that the client decides what info it receives from the server, not the other way around. Whilst many great packages exist to create a Python GraphQL API from scratch, migrating a mature production REST API to use one of these frameworks is not so simple. Not only that, it may be that we are really in love with developing using the Django REST Framework, and don’t want to switch to a new library just to solve this issue.

Applying a GraphQL Layer via GraphWrap

GraphWrap is a python library which, by adding only two lines of code to your Django project, can extend an existing Django Rest Framework API with a GraphQL interface. This is achieved by leveraging Graphene-Django to dynamically build, at runtime, a GraphQL ObjectType for each Django REST view in your API. These ObjectTypes are then glued together to form a GraphQL schema which has the same “shape” as your existing REST API. Note that GraphWrap is not designed to build a GraphQL schema to replace your existing REST API, but rather extend it to offer an additional fully compliant GraphQL-queryable interface.

pip install graph_wrap
from rest_framework import routers

from graph_wrap.django_rest_framework.graphql_view import graphql_view


urlpatterns = [
...,
path(r'/graphql/', view=graphql_view),
]
class BookSerializer(serializers.ModelSerializer):
author = serializers.HyperlinkedRelatedField(
view_name='author-detail', read_only=True)
class Meta:
model = Book
fields = ['author', 'title', 'page_count']
query {
book(id: 1) {
title
author {
name
active
}
}
}
'data': {
'title': 'Lovely Gardens',
'author': {
'name': 'Emilie B.',
'active': True,
} }
  • Since the GraphQL layer is using the REST API under-the-hood, you can be sure that important things like serialization, authentication, authorization and filtering will be consistent between your REST view and the corresponding GraphQL type.

Python Software Developer based in Copenhagen, Denmark