Thinking in GraphQL with Python
Mafinar Khan
Software Developer, Tread Inc
Personal Story
- PyCon Dhaka 2016
- Tread
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