Relay tutorial

Graphene has a number of additional features that are designed to make working with Django really simple.

Note: The code in this quickstart is pulled from the cookbook example app.

A good idea is to check the following things first:

Setup the Django project

We will setup 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 .  # Note the trailing '.' character
cd cookbook
django-admin.py startapp ingredients

Now sync your database for the first time:

python manage.py migrate

Let’s create a few simple models...

Defining our models

Let’s get started with these models:

# cookbook/ingredients/models.py
from django.db import models


class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Ingredient(models.Model):
    name = models.CharField(max_length=100)
    notes = models.TextField()
    category = models.ForeignKey(Category, related_name='ingredients')

    def __str__(self):
        return self.name

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)

Alternatively you can use the Django admin interface to create some data yourself. You’ll need to run the development server (see below), and create a login for yourself too (./manage.py createsuperuser).

Schema

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. In this example, we provide the ability to list all ingredients via all_ingredients, and the ability to obtain a specific ingredient via ingredient.

Create cookbook/ingredients/schema.py and type the following:

# cookbook/ingredients/schema.py
from graphene import relay, ObjectType
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField

from ingredients.models import Category, Ingredient


# Graphene will automatically map the Category model's fields onto the CategoryNode.
# This is configured in the CategoryNode's Meta class (as you can see below)
class CategoryNode(DjangoObjectType):
    class Meta:
        model = Category
        filter_fields = ['name', 'ingredients']
        interfaces = (relay.Node, )


class IngredientNode(DjangoObjectType):
    class Meta:
        model = Ingredient
        # Allow for some more advanced filtering here
        filter_fields = {
            'name': ['exact', 'icontains', 'istartswith'],
            'notes': ['exact', 'icontains'],
            'category': ['exact'],
            'category__name': ['exact'],
        }
        interfaces = (relay.Node, )


class Query(graphene.ObjectType):
    category = relay.Node.Field(CategoryNode)
    all_categories = DjangoFilterConnectionField(CategoryNode)

    ingredient = relay.Node.Field(IngredientNode)
    all_ingredients = DjangoFilterConnectionField(IngredientNode)

The filtering functionality is provided by django-filter. See the usage documentation for details on the format for filter_fields. While optional, this tutorial makes use of this functionality so you will need to install django-filter for this tutorial to work:

pip install django-filter

Note that the above Query class is marked as ‘abstract’. This is because we will now create a project-level query which will combine all our app-level queries.

Create the parent project-level cookbook/schema.py:

import graphene

import ingredients.schema


class Query(ingredients.schema.Query, graphene.ObjectType):
    # This class will inherit from multiple Queries
    # as we begin to add more apps to our project
    pass

schema = graphene.Schema(query=Query)

You can think of this as being something like your top-level urls.py file (although it currently lacks any namespacing).

Testing everything so far

Update settings

Next, install your app and GraphiQL in your Django project. GraphiQL is a web-based integrated development environment to assist in the writing and executing of GraphQL queries. It will provide us with a simple and easy way of testing our cookbook project.

Add ingredients and graphene_django to INSTALLED_APPS in cookbook/settings.py:

INSTALLED_APPS = [
    ...
    # This will also make the `graphql_schema` management command available
    'graphene_django',

    # Install the ingredients app
    'ingredients',
]

And then add the SCHEMA to the GRAPHENE config in cookbook/settings.py:

GRAPHENE = {
    'SCHEMA': 'cookbook.schema.schema'
}

Alternatively, we can specify the schema to be used in the urls definition, as explained below.

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 params with graphiql=True.

from django.conf.urls import url, include
from django.contrib import admin

from graphene_django.views import GraphQLView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
]

If we didn’t specify the target schema in the Django settings file as explained above, we can do so here using:

from django.conf.urls import url, include
from django.contrib import admin

from graphene_django.views import GraphQLView

from cookbook.schema import schema

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^graphql$', GraphQLView.as_view(graphiql=True, schema=schema)),
]

Testing our GraphQL schema

We’re now ready to test the API we’ve built. Let’s fire up the server from the command line.

$ python ./manage.py runserver

Performing system checks...
Django version 1.11, using settings 'cookbook.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Go to localhost:8000/graphql and type your first query!

query {
  allIngredients {
    edges {
      node {
        id,
        name
      }
    }
  }
}

The above will return the names & IDs for all ingredients. But perhaps you want a specific ingredient:

query {
  # Graphene creates globally unique IDs for all objects.
  # You may need to copy this value from the results of the first query
  ingredient(id: "SW5ncmVkaWVudE5vZGU6MQ==") {
    name
  }
}

You can also get each ingredient for each category:

query {
  allCategories {
    edges {
      node {
        name,
        ingredients {
          edges {
            node {
              name
            }
          }
        }
      }
    }
  }
}

Or you can get only ‘meat’ ingredients containing the letter ‘e’:

query {
  # You can also use `category: "CATEGORY GLOBAL ID"`
  allIngredients(name_Icontains: "e", category_Name: "Meat") {
    edges {
      node {
        name
      }
    }
  }
}

Final Steps

We have created a GraphQL endpoint that will work with Relay, but for Relay to work it needs access to a (non python) schema. Instructions to export the schema can be found on the Introspection Schema part of this guide.