Getting started with GraphQL and Graphene

The tech world is always ablaze with buzzwords, and GraphQL has been picking up steam.

Introduced in February 2015 by Facebook, at first I didn't quite understand that adding this layer of abstraction into my web application would be useful. It just seemed to be a new way to write endpoints for your presentation models. But then I took a closer look, and I am now convinced that it will be a core part of future products I develop. Let me explain why with a simple example.

So let's say that you have a Web API written in Django, and in that application you have your business logic and models that reflect the business domain of the application. These models have their relationships and everything is fine and dandy.

However, it might not be the best idea to expose these models directly through an REST endpoint. You might want to have serializers do the authorization so a particular user can't see things in the system (s)he shouldn't. You might want to include extra information that is just not on that model, or include related models in the response.

So in the REST endpoint for /category/1, we would write a view to return a serialized version of the category with id=1. it might return something like this:

{
  "id":1,
  "name":"shoes",
  "product_count":1337
}

Now what if you wanted to get the products of that category? This can be implemented in different ways like
/products?category=1 or '/category/1/products', depending on where you think the responsability of the request belongs.

I think the correct "REST" way would be the '/category/1/products', but in any case this is another endpoint or conditional that have to be written.

GraphQL solves this beautifully by having you define a query schema, where you declare how a certain field will be returned etc -- but we are getting ahead of ourselves!

Firstly, what does a graphQL query look like? Well, something like this:

query {  
  category(id:1) {
    id
    name
    product_count
  }
}

That's it! You can see that the query looks a lot like the shape of the response we want. Each of those fields
id,name, and product_count is defined in the schema ObjectType of category. Each field could be thought of as a function call. When the query gets to the GraphQL endpoint, it will parse the query and look in the schema and try to resolve each field.

As you can see, the category field takes a parameter id with a value of 1, we can use this when resolving a category, in this case just filtering by id.
Then we have the fields id, name, product_count, etc. Those will get resolved to the id, name, and product_count of the first category: category.id, category.name, and category.product_count.

Now let's say you are going to be developing a mobile frontend for your API, and you wanted to display all the products of a Category, but you don't care about the product_count here. Well, instead of writing a new endpoint or modifying the existing one (which might break the current web ui), because we can define the relationship of category and product and how to resolve them in the query schema, we would just write:

query {  
  category(id:1) {
    id
    name
    products {
      name
      rating
    }
  }
}

and fire it of to the GraphQL endpoint. What we would get back is:

{
  "id":"1",
  "name":"shoes",
  "products":[{"name":"converse","rating":"4.5"},{...}]
}

With GraphQL, we can implement any changes in the API without worrying about breaking the endpoints for other client applications. What this gives us is an effective way of doing serialization. Whatever we want to expose, all we need to do is resolve the field in the backend.
Imagine you wanted to allow a user to see only his own posts or comments, but no one else's. We could define an GraphQL ObjectType of viewer which would take a sessionToken as a parameter, and then we just scope all of the posts and comments when getting them through the user with that sessionToken.

What is awesome here is that GraphQL can help decouple your backend from your frontend in a nice way. The frontend developer gets to decide what information to query for.

This eliminates issues of over/under fetching data and a significant amount of inefficient communication between the API team and the frontend team. By moving the responsibility of what should get returned to the frontend, the backend is only concerned
with "how do I resolve this field", enabling adding new features to the backend and then incorporating them where it is appropriate to do so in the frontend.

A real example with Graphene and Django

In this example, we will use Django and assume that we have a Category model with some products.

First, install the GraphQL implementation for Python, which is Graphene. You can install it with: pip install graphene[django], with the Django extensions enabled.

I would also recommend installing Graphiql (pip install graphiql_django -- it's a nice UI for testing out our GraphQL queries.

Now lets create a schema.py for graphene:

import graphene

from AwesomeStore.models import Category, Products

class ProductType(graphene.ObjectType):  
    id = graphene.String()
    name = graphene.String(description='The name of the category')
    rating = graphene.String()


class CategoryType(graphene.ObjectType):  
    id = graphene.String()
    name = graphene.String(description='The name of the category')
    products = graphene.List(ProductType, description='A list of this category's products')
    product_count = graphene.String()

    def resolve_products(self, args, info):
        return self.products.all()

    def resolve_product_count(self, args, info):
        return self.products.all().count()


class QueryType(graphene.ObjectType):  
    all_categories = graphene.List(CategoryType, description='Returns all categories')
    Category = graphene.Field(
        CategoryType,
        id=graphene.ID(),
        description='Just one category belonging to an ID',
    )

    def resolve_all_categories(self, args, info):
        return Category.objects.all()

    def resolve_category(self, args, info):
        id = args.get('id')
        return Category.objects.get(pk=id)

schema = graphene.Schema(query=QueryType)  

Now, we start with a root query type at the bottom, where we can get allcategories or just one by id. Here we can see that graphene with the Django plugin does some magical things to help us out. In the CategoryType model, notice that we don't have a resolve function for the id or name, if graphene can not find a resolve* function, it will try to call it on the model that gets returned by resolve_category in the root query. We still need to define what type it is in the schema, but it's a nice touch to reduce some overhead.

With this in place, we should just be able to add this to our urls.py:

...
from .schema import schema

urlpatterns = [  
    url(r'^graphiql', include('django_graphiql.urls')),
    url(r'^graphql', csrf_exempt(GraphQLView.as_view(schema=schema))),
    ...
]

Now we can go to localhost:8000/graphiql and start testing out our queries.

The graphiql also gives nice hints if you specified a description in the schema definition.

Also notice how the products field on the CategoryType is specified to be of ProductType, and how we resolve that field. The productType is there after a Product model in the Django sense of the word and we can get the name, ratings field, etc. from there.

Hope this brief overview of GraphQl helps you understand the basic problems that it solves. We'll be publishing more posts on other topics such as how to handle mutations and how to integrate with Relay, React and Redux.

comments powered by Disqus