GraphQL & Python – A Beauty in Simplicity, A Beast in Application
Last Updated on: July 14, 2020
Introduced by Facebook in February 2015, GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. It provides a complete, understandable description of the data in your API while giving clients the ability to ask for exactly what they need and nothing more. This gives you the freedom to evolve your APIs over time while accessing powerful developer tools.
Let’s take a closer look at how to connect and fetch data from GraphQL as well as create and update records from Django ORM to graphene object type.
How to Use GraphQL with Django – Getting Started
Setting Up a Django Project
First, let’s start from scratch with a new Django project. Create virtualenv and activate it, then install Django and Graphene. Graphene_django is a Python package that serves Django as GraphQL.
virtualenv env source env/bin/activate pip install django pip install graphene_django
# Setup new project with single app.
django-admin.py startproject graphcool cd graphcool python manage.py startapp graphapp
After generating the app, add the graphapp and graphene_django in installed_apps, then sync the database for the first time:
INSTALLED_APPS = [ ..., “graphene_django”, “graphapp”, ] python manage.py migrate
Next, let’s create a Django model in graphapp/models.py
from __future__ import unicode_literals from django.db import models from django.utils import timezone class Author(models.Model): first_name = models.CharField(max_length=32) last_name = models.CharField(max_length=32) email = models.EmailField() created_at = models.DateTimeField(auto_now=True) def __str__(self): return self.first_name
Next we’ll be setting up a GraphQL backend. To do this, create a file called schema.py inside the app. This is equal to urls.py in the Django project. We’ll define all our queries here and they’ll be handled by the GraphQL backend.
Let’s start with a simple query, like retrieve (get in Django) and list (objects.all in Django)
import graphene import datetime from graphene_django.types import DjangoObjectType from graphapp.models import Author class AuthorType(DjangoObjectType): class Meta: model = Author class Query(graphene.AbstractType): all_authors = graphene.List(AuthorType) author = graphene.Field(AuthorType, id=graphene.Int()) def resolve_all_authors(self, args, context, info): return Author.objects.all() def resolve_author(self, args, context, info): id = args.get('id') if id is not None: return Author.objects.get(pk=id) return None
This schema file is actually an app level schema. Here, the AuthorType class is similar to the serializer file in the Django rest framework which is consolidated into a Query class of AbsractType. The reason for using AbstractType is to allow inheritance later by the Root Query class which will be created in our project level schema file.
So now let’s create our project level schema file (root schema) in graphcool folder — like this: graphcool/schema.py
import graphene import graphapp.schema class Query(graphapp.schema.Query, graphene.ObjectType): pass schema = graphene.Schema(query=Query)
Now, update settings file again to enable GraphQL. Open graphcool/settings.py file and add the following to the file.
GRAPHENE = { 'SCHEMA': 'graphcool.schema.schema' }
Next, import the GraphQLView function from graphene_django views and add it to urls.py file so that queries can be received by django backend.
from django.conf.urls import url 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)), ]
Run server and open localhost:8000/graphql
You will now be able to see the interactive query window in your browser as shown above.
Try the following query in the browser
{ allAuthors{ id firstName lastName } }
You will get a response that looks something like this
{ "data": { "allAuthors": [ { "id": "1", "firstName": "Pawan", "lastName": "Sharma" }, { "id": "2", "firstName": "Shishir", "lastName": "sharma" }, { "id": "3", "firstName": "shirish", "lastName": "sharma" } ] } }
In this example, I’m getting many record details because I have created them from the admin panel. You will get an empty list, so don’t be alarmed if your example output looks different.
Next, let’s start writing mutation for creating new records
Update the app level schema file as follows
import graphene import datetime from graphene_django.types import DjangoObjectType from graphapp.models import Author
class AuthorType(DjangoObjectType): class Meta: model = Author class AddAuthor(graphene.ClientIDMutation): author = graphene.Field(AuthorType) class Input: first_name = graphene.String() last_name = graphene.String() email = graphene.String() @classmethod def mutate_and_get_payload(cls, input, context, info): author = Author( first_name = input.get(‘first_name’), last_name = input.get(‘last_name’), email = input.get(’email’), created_at = datetime.datetime.now(), ) author.save() return AddAuthor(author=author) class AuthorMutation(graphene.AbstractType): add_Author = AddAuthor.Field() class Query(graphene.AbstractType): all_authors = graphene.List(AuthorType) author = graphene.Field(AuthorType, id=graphene.Int()) def resolve_all_authors(self, args, context, info): return Author.objects.all() def resolve_author(self, args, context, info): id = args.get(‘id’) if id is not None: return Author.objects.get(pk=id) return None
Also update the project level schema, and create a new class called RootMutation as follows:
import graphene import graphapp.schema class Query(graphapp.schema.Query, graphene.ObjectType): pass class RootMutation(graphapp.schema.AuthorMutation, graphene.ObjectType): pass schema = graphene.Schema(query=Query, mutation=RootMutation)
Let’s run the following query to add new records in Author.
mutation{ addAuthor(input:{firstName:"Mahendra",lastName:"Dhoni",email:"mahi@example.com"}){ author{ id firstName lastName email } } }
This is the response for the above query
{ "data": { "addAuthor": { "author": { "id": "4", "firstName": "Mahendra", "lastName": "Dhoni", "email": "mahi@example.com" } } } }
Now that we have seen how to create a new record in our database, let’s try to update the record.
In order to do this, we need to create a new mutation method that allows us to update the author information.
Add the following code in graphapp/schema.py
import graphene import datetime from graphene_django.types import DjangoObjectType from graphapp.models import Author class AuthorType(DjangoObjectType): class Meta: model = Author class AddAuthor(graphene.ClientIDMutation): author = graphene.Field(AuthorType) class Input: first_name = graphene.String() last_name = graphene.String() email = graphene.String() @classmethod def mutate_and_get_payload(cls, input, context, info): author = Author( first_name = input.get('first_name'), last_name = input.get('last_name'), email = input.get('email'), created_at = datetime.datetime.now(), ) author.save() return AddAuthor(author=author) class UpdateAuthor(graphene.ClientIDMutation): author = graphene.Field(AuthorType) class Input: id = graphene.String() first_name = graphene.String() last_name = graphene.String() email = graphene.String() @classmethod def mutate_and_get_payload(cls, input, context, info): author = Author.objects.get(pk=input.get('id')) if input.get('first_name'): author.first_name=input.get('first_name') if input.get('last_name'): author.last_name=input.get('last_name') if input.get('email'): author.email=input.get('email') author.save() return UpdateAuthor(author=author) class AuthorMutation(graphene.AbstractType): add_Author = AddAuthor.Field() update_author = UpdateAuthor.Field() class Query(graphene.AbstractType): all_authors = graphene.List(AuthorType) author = graphene.Field(AuthorType, id=graphene.Int()) def resolve_all_authors(self, args, context, info): return Author.objects.all() def resolve_author(self, args, context, info): id = args.get('id') if id is not None: return Author.objects.get(pk=id) return None
The update query is somewhat different than the query for creating a record because it takes the ID of the record to be updated.
So let’s change the query below with input data to be updated.
mutation { updateAuthor(input: {id: "2", firstName: "Shishir"}) { author { id firstName lastName createdAt } } }
You will get the following response:
{ "data": { "updateAuthor": { "author": { "id": "2", "firstName": "Shishir", "lastName": "sharma", "createdAt": "2017-06-13T11:46:39.461389+00:00" } } } }
Should I Use GraphQL over REST?
-
The REST server is about a list of endpoints, while the GraphQL server is like a type system. We define a series of types (in this case, Author but there can be many more types) and the relations between them.
-
GraphQL itself is a simple database language designed to reflect the relationships between these defined types and express fields of interest on each object. This in turn makes GraphQL inherently self-documenting.
-
If, however, you plan to make the API public or collaborative, REST is probably a better option since more developers will be accustomed to working with it.
Stay tuned with us for more such insightful updates.
Good job
Interesting. Graphql is more front-friendly by the way
What’s up, just wanted to mention, I loved this article.
It was inspiring. Keep on posting!