A Brief Introduction to GraphQL in Django

GraphQL is an open source data query and manipulation language. It was created by Facebook in 2012 and publicly released in 2015.
Repository of graphQL: https://github.com/facebook/graphql
It is often described as an alternative to the REST approach.
Some people say that graphQL is not yet production ready compared to the REST.
Comparison of graphQL and REST: https://blog.apollographql.com/graphql-vs-rest-5d425123e34b
While other recommend that migration to graphQL can be easily done and will not bring any huge problems: https://0x2a.sh/from-rest-to-graphql-b4e95e94c26b
On PyCode conference in Warsaw I did lightning talk about graphQL. I asked how many people ever heard of it, out of 40 people about half of them did, but only 2 of the audience members ever used it, none of them in production.
Since it is developed by facebook I’ve seen heavy marketing of graphQL in React community. But none of frontend developers I know is using it right now.
Main features
-
Sending multiple queries in one request
-
Query defines what data should be defined
-
Only one endpoint handles all requests
Examples
Types
All graphQL operations rely on predefined types. It is a definition on how the model would look like, what fields, filters it supports.
type Human {
id: String
name: String
homePlanet: String
}
Query
When we have type we can start to query. For example we need to get all types of Humans but we want to have only their names.
{
hero {
name
}
}
We can also have queries with arguments
{
human(id: "1000") {
name
}
}
Since there are also mutations we might need to include query keyword and name of the operation.
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
In this example we also see that hero object has related objects called friends, we can also include them if we want. Queries can also have variables and directives (include and skip) which might be useful from frontend point of view. More can be found on official documentation: http://graphql.github.io/learn/queries/
Mutations
They allow us to create/update objects. While query fields are executed in parallel, mutation fields run in series, one after the other.
Create review example that uses variables:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
Python
We can use graphQL in Python with graphene package.
Documentation can be found here: https://docs.graphene-python.org/en/latest/
It can be used in Django in similar way Django REST Framework works.
Django
Quick show of how graphene can be used in Django:
More detailed tutorial: https://www.howtographql.com/graphql-python/1-getting-started/
Examples
Models
They stay the same as they were done before. Database type does not matter. GraphQL is often mistaken as a SQL/database language or even database itself. It is not either of them.
Model can look like this:
class Movie(models.Model):
title = models.CharField(max_length=128)
year = models.IntegerField(blank=True)
Types
As mentioned before graphQL operates on types. Most simple type for previous model can look like this:
class MovieType(DjangoObjectType):
class Meta:
model = Movie
We can also include extra type of data which can allow us to add for example filtering.
class MovieType(DjangoObjectType):
class Meta:
model = Movie
filter_fields = {
'title': ['exact', 'icontains', 'istartswith'],
'genre': ['exact', 'icontains'],
'year': ['exact'],
}
For filtering graphene is using django-filter package which needs to be installed separately.
Queries
Before we can start sending requests we need to define queries that can be done.
class MovieQuery(object):
all_movies = graphene.List(MovieType)
def resolve_all_movies(self, info, **kwargs):
return Movie.objects.all()
Such Query will allow us to execute following query in request.
{
all_movies {
title
}
}
We can easily extend that Query to get other cases, for example single movie or one with specific year:
class MovieQuery(object):
movie = graphene.Field(
MovieType,
id=graphene.String(),
title=graphene.String()
)
all_movies = graphene.List(MovieType)
def resolve_movie(self, info, **kwargs):
id = kwargs.get('id')
title = kwargs.get('title')
if id is not None:
return Movie.objects.get(pk=id)
if title is not None:
return Movie.objects.get(title=title)
return None
def resolve_all_movies(self, info, **kwargs):
return Movie.objects.all()
Mutations
Creating Mutations is similar to the queries.
class CreateMovie(graphene.Mutation):
class Arguments:
title = graphene.String(required=True)
year = graphene.Int(required=False)
movie = graphene.Field(lambda: MovieType)
def mutate(self, info, title, year=None):
new_movie = Movie.objects.create(title=title, year=year)
return CreateMovie(movie=new_movie)
Mutation itself defines fields that are required when we want to create object. It also needs to define mutate method.
class MovieMutation(graphene.ObjectType):
create_movie = CreateMovie.Field()
Finally we need to create graphene object so we could later point it to the application schema.
URL and Schema
To make previous examples be available in project we need to complete schema for graphene and set up url.
Setting up schema for queries and mutations:
schema = graphene.Schema(query=MovieQuery, mutation=MovieMutation)
URL pattern for graphene:
url(r'^graphql', GraphQLView.as_view(graphiql=True)),
Graphiql is a visual tool that allows us to browse the allowed queries/mutations and types like an interactive documentation and to execute graphQL code.
Summary
graphQL can surely be seen as an alternative to REST, but it is still not that popular.
Pros:
-
Simplified development on backend side, many parts of code are not needed as they were in DRF, like serializers and views
-
Only one endpoint
-
More freedom on frontend part, no need for new views that include/exclude specific field
-
Multiple queries in one request
Cons:
-
Only one endpoint can be confusing
-
It is a new language for querying that needs to be learn
-
Caching can be currently done only on frontend part of the application
-
No benchmarks/performance comparisons available yet
Example application
I have created example of graphene use in Django. Project can be found here: https://github.com/krzyszti/graphene_example
featured photo by: https://unsplash.com/@ikukevk