This content originally appeared on Twilio Blog and was authored by Adeyemi Atoyegbe
GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. It was developed internally by Facebook in 2012 before being publicly released in 2015. It allows clients to define the structure of the data required, and the same structure of the data is returned from the server, therefore preventing unnecessary data from being returned.
GraphQL has three primary operations: Queries for reading data, Mutations for writing data, and Subscriptions for automatically receiving real-time data updates. A GraphQL server provides clients with a predefined schema – a model of the data that can be requested. The schema serves as common ground between the client and the server.
In this tutorial we will use Graphene, a GraphQL framework for Python, to build a Django API that uses queries and mutations.
Tutorial Requirements
To follow along with this tutorial you should have the following items:
- Python 3.6 or newer.
- Virtualenv, to create a virtual environment for the tutorial project.
- Postman, to send requests to our API.
- A working knowledge of the Django web framework.
Project setup
We will begin by creating a virtual environment and installing necessary Python packages.
Create a folder for our project:
mkdir django_graphql
cd django_graphql
Then create a Python virtual environment and activate it. If you are following this tutorial on Windows:
$ virtualenv env
$ env\Scripts\activate
If you are using a Mac OS or Unix computer:
$ virtualenv env
$ source env/bin/activate
Install the dependencies required for our project:
(env) $ pip install django graphene-django
Create a new django project:
(env) $ django-admin startproject books_api
Change your current directory to the project:
(env) $ cd books_api
Create a api app in the books_api project
(env) $ python manage.py startapp api
Next register the api app and integrate the graphene-django third-party app we installed earlier into our books_api project. Find the INSTALLED_APPS list In books_api/settings.py and add api and `graphene-django’ at the end:
INSTALLED_APPS = [
...
'api',
'graphene_django',
]
While in books_api/settings.py, go to the bottom of the file and add a GRAPHENE dictionary with settings for the graphene-django package:
GRAPHENE = {
"SCHEMA": "api.schema.schema"
}
The SCHEMA setting tells Graphene where to find the GraphQL schema for the application. We’ll define the schema after we have the database created.
Database model
Open the api/models.py file and type in the code below to add the Book database model:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
year_published = models.CharField(max_length=10)
review = models.PositiveIntegerField()
def __str__(self):
return self.title
Then create and run the migrations for our database:
$ python manage.py makemigrations
$ python manage.py migrate
To help in testing this project we can now populate our database with some data. To do this, you create a data.json file in the project directory where the manage.py file is, and copy the following data into it:
[
{
"model": "api.book",
"pk": 1,
"fields": {
"title": "The One Thing",
"author": "",
"year_published": 2002,
"review": 3
}
},
{
"model": "api.book",
"pk": 2,
"fields": {
"title": "Python Crash Course",
"author": "Eric Matthes",
"year_published": 2015,
"review": 5
}
},
{
"model": "api.book",
"pk": 3,
"fields": {
"title": "Atomic Habits",
"author": "James Clear",
"year_published": 2002,
"review": 4
}
},
{
"model": "api.book",
"pk": 4,
"fields": {
"title": "The Compound Effect",
"author": "Darren Hardy",
"year_published": 2010,
"review": 3
}
},
{
"model": "api.book",
"pk": 5,
"fields": {
"title": "Clean Code",
"author": "Robert Cecil Martin",
"year_published": 2008,
"review": 4
}
}
]
With the data.json file saved to the current directory, run the command below to import the data into the database:
$ python manage.py loaddata data.json
Installed 5 object(s) from 1 fixture(s)
Next, add the GraphQL endpoint at the end of the urlpatterns dictionary in file books_api/urls.py:
from django.contrib import admin
from django.urls import path
from graphene_django.views import GraphQLView
urlpatterns = [
path('admin/', admin.site.urls),
path('graphql', GraphQLView.as_view(graphiql=True)),
]
Building a Books API with GraphQL
In this section we will be building an API with Graphene using GraphQL queries and mutations.
Implementing a GraphQL Schema
Create a new file in the api/schema.py folder:
import graphene
from graphene_django import DjangoObjectType, DjangoListField
from .models import Book
class BookType(DjangoObjectType):
class Meta:
model = Book
fields = "__all__"
class Query(graphene.ObjectType):
all_books = graphene.List(BookType)
book = graphene.Field(BookType, book_id=graphene.Int())
def resolve_all_books(self, info, **kwargs):
return Book.objects.all()
def resolve_book(self, info, book_id):
return Book.objects.get(pk=book_id)
In this step we have created two classes, The first one is BookType, which adapts the Book model to a DjangoObjectType. We set fields to __all__ to indicate that we want all the fields in the model available in our API.
The Query class defines the GraphQL queries that the API will provide to clients. The all_books query will return a list of all the BookType instances, while the book query will return one BookType instance, given by an integer ID. The class defines two methods, which are the query “resolvers”. Every query in the schema maps to a resolver method.
The two query resolvers query the database using the Django model to execute the query and return the results.
Adding data updates with GraphQL mutations
We will now add create, update and delete operations through mutations. While still in the api/schema.py file, add the code below at the bottom:
class BookInput(graphene.InputObjectType):
id = graphene.ID()
title = graphene.String()
author = graphene.String()
year_published = graphene.String()
review = graphene.Int()
The BookInput class defines fields similar to our Book model object to allow the client to add or change the data through the API. We will use this class as an argument for our mutation classes.
Let’s add a mutation to create new books. Add the following code at the bottom of api/schema.py:
class CreateBook(graphene.Mutation):
class Arguments:
book_data = BookInput(required=True)
book = graphene.Field(BookType)
@staticmethod
def mutate(root, info, book_data=None):
book_instance = Book(
title=book_data.title,
author=book_data.author,
year_published=book_data.year_published,
review=book_data.review
)
book_instance.save()
return CreateBook(book=book_instance)
The CreateBook class will be used to create and save new Book entries to the database. For every mutation class we must have an Arguments inner class and a mutate() class method.
We defined an instance of the BookInput class we created earlier as our arguments, and we made it mandatory with the required=True option. After that we defined the model we are working with by doing this book = graphene.Field(BookType).
In the mutate method we are saving a new book by calling the save() method on a new Book instance created from the book_data values passed as argument.
Below you can see the implementation of the UpdateBook mutation. Add this code at the bottom of api/schema.py:
class UpdateBook(graphene.Mutation):
class Arguments:
book_data = BookInput(required=True)
book = graphene.Field(BookType)
@staticmethod
def mutate(root, info, book_data=None):
book_instance = Book.objects.get(pk=book_data.id)
if book_instance:
book_instance.title = book_data.title
book_instance.author = book_data.author
book_instance.year_published = book_data.year_published
book_instance.review = book_data.review
book_instance.save()
return UpdateBook(book=book_instance)
return UpdateBook(book=None)
The UpdateBook mutation class is very similar to CreateBook. The difference here is the logic in the mutate() method, which retrieves a particular book object from the database by the book ID provided and then applies the changes from the input argument to it.
Finally, let’s add a delete mutation. Add the code that follows at the bottom of api/schema.py:
class DeleteBook(graphene.Mutation):
class Arguments:
id = graphene.ID()
book = graphene.Field(BookType)
@staticmethod
def mutate(root, info, id):
book_instance = Book.objects.get(pk=id)
book_instance.delete()
return None
In the DeleteBook mutation class we have graphene.ID as the only argument. The mutate() method uses this id to remove the referenced book from the database.
We now have two queries and three mutations defined. To register these with Graphene, add the code below at the end of api/schema.py:
class Mutation(graphene.ObjectType):
create_book = CreateBook.Field()
update_book = UpdateBook.Field()
delete_book = DeleteBook.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
Testing the GraphQL API
We are not ready to test the API. Let’s start the Django server:
$ python manage.py runserver
Now visit http://127.0.0.1:8000/graphql in your browser. You should see the GraphIQL interface for interactive testing of the GraphQL API.

The black arrow in the diagram above is where you input your GraphQL code. Then you click on the play button in the top left corner of the screen to run the code and get a result in the area indicated with the blue arrow.
Issuing a query
Queries are used to request data from the server. The GraphQL code below is requesting all the books from the database. Enter it in the left-side panel of the GraphIQL interface.
query {
allBooks {
id
title
author
yearPublished
review
}
}
Press the play button to execute the query and see the results in the right-side panel.
Next try the following query, which requests a single book by its id:
query {
book(bookId: 2) {
id
title
author
}
}
Note how each query can specify which of the attributes of the book model need to be returned.
Creating a book
The following GraphQL snippet defines a mutation that adds a new book to the database:
mutation createMutation {
createBook(bookData: {title: "Things Apart", author: "Chinua Achebe", yearPublished: "1985", review: 3}) {
book {
title,
author,
yearPublished,
review
}
}
}
Updating an existing book
The next GraphQL mutation updates the book with id=6:
mutation updateMutation {
updateBook(bookData: {id: 6, title: "Things Fall Apart", author: "Chinua Achebe", yearPublished: "1958", review: 5}) {
book {
title,
author,
yearPublished,
review
}
}
}
Deleting a book
The final mutation example deletes the book with id=6 from the database:
mutation deleteMutation{
deleteBook(id: 6) {
book {
id
}
}
}
Testing the Book API with other GraphQL clients
Django CSRF prevents unauthenticated users on the website from performing malicious attacks. Given this, any POST request made from an external application outside the Django site will result in a 403 Forbidden Error.
To avoid this there are two options. The most secure option is to add the CSRF token generated by the Django application to the POST requests that your client makes to the GraphQL endpoint. See the Ajax section in the Django documentation to learn about this option.
An easier, but less secure option is to exempt the GraphQL endpoint from CSRF protection. To do that, open theapi/urls.py file and change the definition of the GraphQL endpoint as follows:
from django.urls import path
from graphene_django.views import GraphQLView
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
...
path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
The csrf_exempt wrapper added to the GraphQLView removes the CSRF token verification from the endpoint.
If you want to make sure the CSRF protection does not interfere with your GraphQL endpoint, you can use Postman to send GraphQL requests to the Django API:

Using the above screenshot as a reference, follow these steps to send a GraphQL request with Postman:
- Paste your GraphQL endpoint
http://127.0.0.1:8000/graphqlin the box with the purple arrow. - Click on the first white arrow pointing to the “Body” option
- Click on the GraphQL options at the second white arrow
- Paste your query code in the query box and click on the “Send” blue button.
- You will see the result of your API request at the bottom, in the light green arrow area.
- Notice the blue arrow area, where you should be making a GET request for queries, or a POST request for mutations.
Try the code snippets we used above to test our API through Postman.
Conclusion
In this tutorial we have created a simple GraphQL API in Django using the Graphene-Django package built on top of Graphene, which made it easy to add GraphQL functionality to our Django application.
We created queries to read data, mutations to write and change data and tested our API using the GraphIQL interface provided by the Graphene-Django library and the popular API client Postman. You can find the complete code for the project here.
Adeyemi Atoyegbe is a self-taught Python developer. You can find him on Twitter, GitHub.
This content originally appeared on Twilio Blog and was authored by Adeyemi Atoyegbe
Adeyemi Atoyegbe | Sciencx (2021-03-26T13:03:57+00:00) Building GraphQL APIs in Django with Graphene. Retrieved from https://www.scien.cx/2021/03/26/building-graphql-apis-in-django-with-graphene/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.