Building a GraphQL API with Python & Django

In this tutorial, youll implement your own GraphQL server in python and graphene.



Introduction

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:
  • A Django project called cookbook
  • An app within cookbook called ingredients
# 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 migrate

Lets run the server:
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:


. . .

Defining our models

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

Load some test data
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)

. . .


Schema and Object Types

In order to make queries to our Django project, we are going to need few things:
  • Schema with defined object types
  • A view, taking queries as input and returning the result
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:

Testing Our API

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.
. . .

Writing Queries

Now we are going to write Queries. We will write two queries :
  • all_ingredients - it will retuern all ingredients from database
  • all_categories - it will retuern all categories from database

Edit ingredients/schema.py as follows:


. . .

. . .

Lets test our Queries
Lets test our Queries

. . .

Writing Mutations

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:


. . .

Error Handling

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)

. . .

Conclusion

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.

If you'd like to see the source code of the complete application, you can find it here.

Never miss a post from Eric Leslie, when you sign up for Ednsquare.