GraphQL query language by examples

Django REST APIs developer perspective

Radoslaw Kowalski
10Clouds
8 min readNov 25, 2019

--

GraphQL is the new hot thing right now, bursting into the market as an alternative to REST architecture, solving lots of its problems but also bringing its own. It’s already been adopted by a host of big names, including Facebook, GitHub, PayPal and the New York Times.

At 10Clouds, we decided it’s high time that we tested this new query language, so we set ourselves a simple task: to prepare a proof of concept app aimed at exploring the joys and sorrows of implementing GraphQL API in Django. We wanted to share our findings with you.

This article is for you if:

  • You want to find out the basics of GraphQL
  • You want to understand its benefits and challenges
  • You’re wondering how it compares to REST

Why me?

  • I work as a Python developer at 10Clouds, and am working on Django backends for Web Services, mainly with REST APIs using Django Rest Framework
  • We always aim to be at the cutting edge of the latest dev developments and to test them on practical examples
  • I take part mostly in startup and new projects, with constantly changing and developing APIs, requiring fast and easy to maintain solutions, like GraphQL

Before diving into the details of the aforementioned project and how GraphQL works with Django, I wanted to shed some light on what exactly GraphQL is, to show its differences from REST and present a brief comparison of the two.

GraphQL and REST

GraphQL is a data query language developed by Facebook in 2012 for internal use and open sourced in 2015. It strives to be the new standard in modern development, focusing on productivity and minimizing data transfer by providing queries where the client decides exactly what he gets.

Using simple yet powerful query language, rather than resting (pun intended) on uncountable endpoints, GraphQL allows focusing on domain and providing data as clear and extensible structure without need to optimize each endpoint separately for specific needs, as happens to be the case with REST APIs.

Let’s imagine the classic example of library where we have Books and Authors (defined as OpenAPI 3 specification):

Simple library schema in OpenAPI 3 specification

RESTful approach

With REST we would probably like to have endpoints similar to:

  • api/authors for list of Authors
  • api/authors/{author_id} for specific Author
  • api/authors/{author_id}/books for a list of Books written by specific Author
  • api/books?category=&language= for a list of all Books, maybe filtered by category or language
  • api/books/{book_id} for specific Book

Now let’s imagine we have one view that uses only titles of Books from specific author and second that needs their details. Should we always use api/authors/{author_id}/books and in the first case just ignore additional data, or should we introduce a new endpoint? Or maybe include list of Author’s Books in api/authors/{author_id} and list details in the previous endpoint? But what if details about Author are not required either? I can keep going with such examples, but I guess the point is clear: with REST API you focus on API structure more than data structure.

Tackling it with GraphQL

With GraphQL, at least for me, the most important thing is the domain. That can be a big positive but also a pain in the neck at times. Our aim should be on representing the domain as clearly as possible, remembering about possible growth in the future. With REST, growth would probably mean refactor, breaking API changes and more and more endpoints. But with GraphQL it should be as easy as adding new fields in existing types allowing for more advanced queries. We’ll talk more about queries in a moment. But first, we have to define types as GraphQL is all about types and clear declaration of things. Because of this, it is not tied to any specific database/storage, all that matters is the data you define (hello there again, domain).

Object types, fields and arguments
Without further ado let’s see how our Book and Author look as GraphQL types, written in what’s called GraphQL schema language:

Simple library schema in GraphQL schema language

So now we have our types, note the Country type that would allow for later extension (remember the domain and growth?). Each field also has a type, GraphQL has built-in scalar types such as: Int, Float, String, Boolean and ID.

Fields and arguments can be marked as non-nullable (required) by ! (like in name: String!), arguments without ! are optional. Arguments can take default value if it is optional and no value was passed: to have EN for language argument in Author field books it should be declared as books(language: BookLanguage = EN).

Let’s quickly go through the defined schema:

  • Country: a GraphQL Object Type, it represents a type with some fields (name in this case),
  • name: field on the Country type, it is the only field on this type and only it can appear in GraphQL queries on the Country type,
  • BookCategory and BookLanguage: enumeration types limiting value choices to given set, how they are mapped internally in the implementation language is not a concern, for the client they will always be from those sets,
  • Date: user defined scalar type to represent date, service has to define how it should be serialized, deserialized and validated (for our example let’s assume it is ISO 8601 formatted string),
  • Author: Object Type representing our Author model,
  • id, name: fields on Author type, as their types are marked with ! GraphQL service promises to always return a value for those fields,
  • birthday, country: optional fields on Author type, that may be null,
  • books: field on Author type accepting arguments, every GraphQL object type’s field can have 0 or more arguments, all arguments are named, in this case they are category of type BookCategory and language of type BookLanguage,
  • [Book]!: return type of books field that is an array of Book objects ([<type>]! means that client can always expect an array of 0 or more <type> values, but not null, [<type>!] on the other hand would mean that array with at least one Book object is returned or null),
  • ID: special type that represents unique key used to identify object, represented as a String,
  • Book: Object Type representing our Book model,
  • id, author, title, language: not-null fields on Book type,
  • pages, category: optional fields on Book type.

The Query type
One more type is required, one that will define the entry points to our API — a root type. It is called Query and could look like the following sequence:

Simple library root Query type object

When a field is executed, the corresponding resolver function is called to produce the next value. Developer should provide resolvers for each field on each type, especially ones with arguments as for simple fields — that map to the model — used library would probably ‘resolve’ that for us. Let’s just assume they are done in an intuitive way, where a null argument means no filtering and providing argument filters results to ones with this value. Implementing GraphQL service using library in any language will probably tackle most of them for you, like django-graphene for Django framework in Python.

Writing GraphQL queries
Each GraphQL query consists of multiple nested objects. The root object (external {}) can be interpreted as the Query type query. Then, using fields on Query type, we ask for specific data — selecting fields for each object from Query’s fields, then fields of those objects and so on… As long as returned data is an object, fields to return have to be provided explicitly.

Finally, how would we provide functionality reflecting the REST endpoints from above using GraphQL query language? They could look something like (for simplicity the JSON responses are included in the same gist as GraphQL queries):

  • for list of Authors where we ask for authors field on root object, selecting id and name fields from returned Author objects:
  • selecting specific fields of received data is first big advantage of GraphQL over REST, using same authors field we could ask for all the details, simply by adding remaining fields to query:
  • but wait, there’s more… we have books field on Author object type which means we could also get list of author’s books in a single query. To get list of authors with their names and books titles:
  • one more thingbooks takes arguments, so what if we want to display only novels in english, grouped by author? Ask for authors and pass arguments to books , NOVEL and EN are enum type values, so they are provided like keywords — without quotation marks
  • for specific Author, for example to display his profile page, we could ask for details of author with 1 as ID:
notice that id is a String as ID type is based on String even if database stores integers
  • but that is not much for a profile, right? why don’t we ask for more details, in single request, maybe a list of books in each category? in GraphQL query language you can request each field multiple times, providing an alias:
with aliases you could easily also add books grouping by language
  • for a list of all Books, maybe filtered by category or language we would use books field or the root query type, resolving it almost the same like books field in Author but not limiting response to specific author:
notice that we have to provide what author field should include as it is of Author object type
  • for specific Book we defined book field on root query type:

With all those tools, problems such as

Now let’s imagine we have one view that uses only titles of Books from specific author and second that needs their details.

seems trivial, and in fact they are, it is only a matter of requesting additional fields for books field on author, all using single implementation of author query:

As you can see in the example above, multiple queries can be send in one request as query is an object type itself and can have it’s fields aliased. So all of the queries presented above could have been single request to the service, returning all required data in one response data object. More complex query, in this case for bookshop localization, could look like:

Returning not only authors, but also all books by category.

What about data writing?
Now we know how to request data from the service, what types are supported out-of-the-box, how to define own GraphQL Object Types and arguments on fields.

But what about creating and updating objects? Here comes Mutation type which is at the same level as Query type in schema, and is a second entry point to the service:

schema {
query: Query
mutation: Mutation
}

More information on mutations can be found in the official specification and guide to GraphQL, which I highly recommend as a source of knowledge.

In summary…

It is important to remember that GraphQL in itself is only a specification of query language — it does not provide or query the data itself. We have to implement GraphQL API providing service in some programming language — e.g. Python, or in my case that means Django framework.

One of the choices for Python library to build GraphQL APIs is Graphene-Python. It supports integration with frameworks like Django (Graphene-Django), SQLAlchemy (Graphene-SQLAlchemy) and Google App Engine (Graphene-GAE).

This is great news for us, as in means that there is a ready solution for bringing GraphQL powers to Django apps. Graphene-Django to the rescue, coming in follow-up article very shortly.

Further reading

Other, but not all, important topics not covered in this article are:

  • fragments — making complex queries easy to write,
  • interfaces — abstracting set of required fields for multiple implementations,
  • union types — abstract class to represent multiple others, without specifying any common fields,
  • input types — complex objects as arguments into a field,
  • meta fields.

Good summary of benefits and obstacles that GraphQL brings into products can be found in an article from Netflix about their journey of aggregating multiple REST APIs with single GraphQL API.

Looking at creating your own project in GraphQL? Want to find out more about our work on GraphQL or a range of other platforms? Why not drop us an email on hello@10clouds.com or visit our website: www.10clouds.com

--

--