Thinking in GraphQL with Python

Mafinar Khan

Software Developer, Tread Inc

Personal Story

Part I, the Concepts

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data.

  • One system queries another for data
  • A strongly typed sub-language that makes it happen
  • Thereby establishing an API between *-ends
  • Server-side environment sits at the surface of the back-end
  • Client-side facilitates powerful data-driven UI capability

Er.. REST?

Let's think about thinking

GraphQL

  • Think about data as a graph
  • And your front-end designer treats it like a database
  • Reads from it, Writes to it
  • URL is like a Database Connection
  • And clients have an SQL like Query

Does it sound like REST?

But they have similar objective(s)

f(data) = UI

What if there is a way to query the data easily no matter how complex?

AND COLOCATE WITH BACKEND?

to expand this...

  • Client sends the query to the server
  • The server parses it
  • Resolves the fields
  • Pulls out data from data-sources
  • Matches with client's demands
    • Or screams at it for error/mismatches
  • Sends out the data
  • Profit!

Think of it as an empty form that clients sends, server fills it up and returns
You send only what you need

Client

Server

Query/Mutation

GraphQL

  • Ask only what you need
  • Know before hand what you'll get
  • OMG Graphiql

DEMO TIME

Part II, Enter Python

Y no Client?

GraphQL Server?

Introducing Graphene

Graphene

  • Awesome API
  • Easy to Reason About
  • Pretty DRY

It all starts with ObjectTypes, and then some Types 

ObjectTypes

  • Source of Data
  • Think Django Model, only Graph theoretically inclined
  • Contains fields, strongly typed fields, just like Django models
  • If you have a field, RESOLVE IT!

Fields

  • Strongly Typed, with Arguments
  • Scalars, Lists and NonNulls
  • Can contains arguments as more types
  • Resolve!
import graphene


class Person(graphene.ObjectType):
    first_name = graphene.String()
    last_name = graphene.String()
    full_name = graphene.String()

    def resolve_full_name(self, info):
        return '{} {}'.format(
            self.first_name, self.last_name)

Mutations

  • Used for Data Changes/Writes
  • Has fields, as in what it will return back
  • Has Arguments, the data it needs to write to
  • That mutate method
import graphene


class CreatePerson(graphene.Mutation):
    class Arguments:
        name = graphene.String()

    ok = graphene.Boolean()
    person = graphene.Field(lambda: Person)

    def mutate(self, info, name):
        person = Person(name=name)
        ok = True
        return CreatePerson(
                person=person, ok=ok)

Schema

Query? Mutation? Type?

schema = schema.Schema(query=RootQueries, mutation=RootMutation)
schema.execute(GRAPHQL_QUERY)

Enter Django!!!

Why Django Graphene?

  • DjangoObjectType knows Django models, DRY!!!
  • Nice view onliner that gives you the whole thing
  • Hate to write mutation? Love DRF? CHECK!
  • Middleware (especially that Debug one!)
  • OMG GraphiQL

work flow

  • The settings.py ritual
  • Add the View
    • GraphQLAPIView
    • Wrap your own
      • Security?
  • Compose queries, mutations as Root
  • Add to Schema
  • Enjoy!
class User(DjangoObjectType):
    class Meta:
        model = UserModel


class Query(graphene.ObjectType):
    users = graphene.List(User)

    def resolve_users(self):
        return UserModel.objects.all()


schema = graphene.Schema(query=Query)

Serializer for Mutation?

from graphene_django.rest_framework.mutation import SerializerMutation


class UserMutation(SerializerMutation):
    class Meta:
        serializer_class = UserSerializer

Filters? Connections?

Got to use Relay!

class AnimalNode(DjangoObjectType):
    class Meta:
        # Assume you have an Animal model defined with the following fields
        model = Animal
        filter_fields = ['name', 'genus', 'is_domesticated']
        interfaces = (relay.Node, )

class Query(ObjectType):
    animal = relay.Node.Field(AnimalNode)
    all_animals = DjangoFilterConnectionField(AnimalNode)
query {
  # Note that fields names become camelcased
  allAnimals(genus: "cat", isDomesticated: true) {
    edges {
      node {
        id,
        name
      }
    }
  }
}

No Relay?

Code!!!

Thinking in GraphQL with Python

By Mafinar Khan

Thinking in GraphQL with Python

  • 1,708