Django up to GraphQL [Part:1]

GraphQL is great! It gives you the power to build a complete new way to interact with an API. Imagine you go to a restaurant and you order some food and you want something extra or remove some ingredient but the waiter says no you can't you must to take it without any change maybe you can accept but remove want you don't want by your own but it is a waste of time and food!. Working with API REST this is the rule. You have a resource and that's it. The response will give you allways the same elements even if you don't needed.

Let's make an example. Imagine a ToDo List app so you must have users and tasks so maybe we have something like this.

GET /user/1

{
    'username': 'carlosmart',
    'name': 'Carlos Martinez',
    'email': 'me@carlosmart.co',
    'task_count': 10,
}

GET /user/1/tasks

[
    {
        'name': 'Write a GraphQL post',
        'created': '2018-07-11',
        'is_done': false
    },
    {
        'name': 'Go to Medellín',
        'created': '2018-07-11',
        'is_done': false
    }
]

Great this should work to create a simlpe ToDo List but what if you have a different view in a mobile app and you only need the user's name and no more information to display or your need to show at the sametime the user information and the user's tasks? In the fist case you're not using all the values than the API provides but in the second case is worse you'll need to make two requests!

How do you get the data from GraphQL?

The next example is how to get the list of users in django.

POST /graphiql/)

query {
  users{
    edges{
      node{
        username
        firstName
        lastName
      }
    }
  }
}

This is going to be the response.

{
  "data": {
    "users": {
      "edges": [
        {
          "node": {
            "username": "carlosmart",
            "firstName": "Carlos",
            "lastName": "Martinez"
          }
        },
        {
          "node": {
            "username": "other",
            "firstName": "Other",
            "lastName": "User"
          }
        }
      ]
    }
  }
}

But what if we need just the username?

POST /graphiql/)

query {
  users{
    edges{
      node{
        username
      }
    }
  }
}

Response

{
  "data": {
    "users": {
      "edges": [
        {
          "node": {
            "username": "carlosmart"
          }
        },
        {
          "node": {
            "username": "other"
          }
        }
      ]
    }
  }
}

How to get a particular user?

Graphql in Django by default uses a "UserNode:{Id}" encoded in base64 to define the id, so to get information about one particular user this is the query.

POST /graphiql/)

query {
  user(id: "VXNlck5vZGU6MQ=="){
    username,
    firstName
    lastName
    profile{
      birthDate
    }
  }
}

Response

{
  "data": {
    "user": {
      "username": "carlosmart",
      "firstName": "Carlos",
      "lastName": "Martinez",
      "profile": {
        "birthDate": "1989-08-06"
      }
    }
  }
}

How to use GraphQL inside Django?

Use GraphQl in Django is pretty similar to use Django Rest Framework(DRF) but using this library.

Installation

pip install "graphene-django>=2.0"

Settings

INSTALLED_APPS = (
    # ...
    'graphene_django',
)

GRAPHENE = {
    'SCHEMA': 'your_project.schema.schema',
}

Urls

from django.conf.urls import url
from graphene_django.views import GraphQLView

urlpatterns = [
    # ...
    url(r'^graphql', GraphQLView.as_view(graphiql=True)),
]

Define models

from django.db import models
from django.contrib.auth.models import User


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

    def __str__(self):
        return f'{self.user}'

Define a Node for each model

import graphene
from graphene import relay

from .models import Profile


class ProfileNode(DjangoObjectType):

    class Meta:
        model = Profile
        exclude_fields = ()
        interfaces = (relay.Node, )

Define general Schema

import graphene

from graphene import relay
from graphene_django.filter import DjangoFilterConnectionField
from graphene_django.debug import DjangoDebug

from courses.schemas import CourseNode
from profiles.schemas import ProfileNode, UserNode
from profiles.mutations import UpdateProfile, LoginUser
from courses.mutations import CreateCourse, UpdateCourse


class Query(graphene.ObjectType):
    hello = graphene.String()
    user = relay.Node.Field(UserNode)
    users = DjangoFilterConnectionField(UserNode)

    course = relay.Node.Field(CourseNode)
    courses = DjangoFilterConnectionField(CourseNode)

    me = graphene.Field(UserNode)

    debug = graphene.Field(DjangoDebug, name='__debug')

    def resolve_me(self, info):
        return UserNode.get_node(info, info.context.user.id)

    def resolve_hello(self, info, **kwargs):
        return 'world'

Examples:

What's next?

This is the first post about Django and GraphQL the following posts I'll wrinte about:

Let's keep in touch in Twitter / Instagram or send me an email.