Building a GraphQL API with Python & Django
In this tutorial, youll implement your own GraphQL server in python and graphene.
Web APIs are the engines that power most of our applications today. For many years REST has been the dominant architecture for APIs, but in this article we will explore GraphQL. With REST APIs, you generally create URLs for every object of data that's accessible. Let's say we're building a REST API for movies - we'll have URLs for the movies themselves, actors, awards, directors, producers... it's already getting unwieldy! This could mean a lot of requests for one batch of related data. Imagine you were the user of a low powered mobile phone over a slow internet connection, this situation isn't ideal.
GraphQL is not an API architecture like REST, it's a language that allows us to share related data in a much easier fashion. We'll use it to design an API for movies. Afterwards, we'll look at how the Graphene library enables us to build APIs in Python by making a movie API with Django. Set up the Django project
We will set up the project, create the following:
# Create the project directory
mkdir cookbook
cd cookbook
# Create a virtualenv to isolate our package dependencies locally
virtualenv env
source env/bin/activate # On Windows use `env\Scripts\activate`
# Install Django and Graphene with Django support
pip install django
pip install graphene_django
# Set up a new project with a single application
django-admin.py startproject cookbook
cd cookbook
django-admin.py startapp ingredients
Now sync your database for the first time:
python manage.py runserver
The commands above will install the necessary libraries, create a new Django project, create the base Django tables and start its builtin web server. To test it, open your browser and access the http://localhost:8000address. You should see the following page:
Let’s create a few simple models... Let’s get started with these models:
Don’t forget to create & run migrations:
python manage.py makemigrations
python manage.py migrate
Now is a good time to load up some test data. The easiest option will be to download the ingredients.json fixture and place it in cookbook/ingredients/fixtures/ingredients.json. You can then run the following: $ python ./manage.py loaddata ingredients
Installed 6 object(s) from 1 fixture(s)
In order to make queries to our Django project, we are going to need few things:
GraphQL presents your objects to the world as a graph structure rather than a more hierarchical structure to which you may be accustomed. In order to create this representation, Graphene needs to know about each type of object which will appear in the graph.
This graph also has a root type through which all access begins. This is the Query class below.
This means, for each of our models, we are going to create a type, subclassing DjangoObjectType
After we’ve done that, we will list those types as fields in the Query class.
Create cookbook/ingredients/schema.py and type the following:
Note that the above Query class is a mixin, inheriting from object. This is because we will now create a project-level query class which will combine all our app-level mixins.
Create the parent project-level cookbook/schema.py:
Configuring Graphene Django
In the cookbook/settings.py file, search for the INSTALLED_APPS variable and add the following:
Add ingredients as INSTALLED_APPS:
INSTALLED_APPS = [
# Other apps
'graphene_django',
'ingredients', # Install the ingredients app
]
And add following at the bottom of the file cookbook/settings.py file:
GRAPHENE = { 'SCHEMA': 'cookbook.schema.schema', }
Creating GraphQL and GraphiQL views
Unlike a RESTful API, there is only a single URL from which GraphQL is accessed. Requests to this URL are handled by Graphene’s GraphQLView view.
This view will serve as GraphQL endpoint. As we want to have the aforementioned GraphiQL we specify that on the parameters with graphiql=True. Edit cookbook/urls.py as follow:
To test our API, let's run the project and then go to the GraphQL endpoint. In the terminal type:
python manage.py runserver
Once your server is running head to http://127.0.0.1:8000/graphql/. You'll encounter GraphiQL- a built in IDE to run your queries! Now graphql server is up and running.
Now we are going to write Queries. We will write two queries :
Edit ingredients/schema.py as follows:

Lets test our Queries
The process of sending data to server is called mutation. Defining it is pretty similar on how you’ve defined the query.
With Graphene-Django we can take advantage of pre-existing Django features to quickly build CRUD functionality, while still using the core graphene mutation features to add custom mutations to a Django project. #1
class AddCategory(graphene.Mutation):
#2
class Arguments:
# The input arguments for this mutation
categoryName = graphene.String(required=True)
# The class attributes define the response of the mutation
category = graphene.Field(CategoryType)
#3
def mutate(self, info, categoryName):
_category = Category.objects.create(name=categoryName)
# Notice we return an instance of this mutation
return AddCategory(category=_category)
#4
class Mutation(object):
add_category = AddCategory.Field()
Let me explain this snippet, piece by piece:
#1: Defines a mutation class. Right after, you define the output of the mutation, the data the server can send back to the client. The output is defined field by field for learning purposes. In the next mutation you’ll define them as just one.
#2: Defines the data you can send to the server, in this case, the links’ url and description.
#3: The mutation method: it creates a link in the database using the data sent by the user, through the urland description parameters. After, the server returns the CreateLink class with the data just created. See how this matches the parameters set on #1.
#4: Creates a mutation class with a field to be resolved, which points to our mutation defined before.
Here is complete ingredients/schema.py as follows:
Edit cookbook/schema.py to include Mutation. Here is final schema.py looks liks:
All applications fail, and GraphQL is no different. Some clients may ask for information that’s not available or execute a forbidden action. In this chapter, you’ll understand how GraphQL and Graphene address these issues.
Schema Errors
Being a language with a strong type system, GraphQL can predetermine if a query is valid. All the fields from queries and mutations have a strong type, so requesting and inputting wrong data will generate an error.
Try it out! In the links query, ask for the cheese field and see how GraphQL returns back an error:
Graphene Errors
On the application level, you can use the GraphQLError class or the good and old Python exceptions. class AddCategory(graphene.Mutation):
class Arguments:
# The input arguments for this mutation
categoryName = graphene.String(required=True)
# The class attributes define the response of the mutation
category = graphene.Field(CategoryType)
def mutate(self, info, categoryName):
_category = Category.objects.create(name=categoryName)
if not _category:
raise GraphQLError("An error occurred while adding category")
# Notice we return an instance of this mutation
return AddCategory(category=_category)
GraphQL is a strongly-typed query language that helps to create evolvable APIs. We designed an API schema for movies, creating the necessary types, queries and mutations needed to get and change data.
With Graphene we can use Django to create GraphQL APIs. We implemented the movie schema we designed earlier and tested it using GraphQL queries via GraphiQL and a standard POST request.