GraphQL is an interesting technology with a lot of good points, but some drawbacks.
Pros:
- Self explorable
- easy documentation
- intuitive
- simplifies client side logic
Cons:
- Graphene is the only library and still young.
- v1.0 released in Sept2016 and not very updated.
- Docs are not very good
- Known bugs with resolver – the order graphene does it by default is wrong
- Source code is complicated with lots of meta-programming, so hard to update
- Authorization
- Denial of Service
- Performance!
Performance issues with REST
Showing a lot of related data requires lots of requests
Serialization takes a lot of time, especially with REST resources that contain a lot of information that isn’t used on most of the pages
Dashboards Challenge
GraphQL vs REST! 5 s vs 10s result even after reducing the data sent by the REST.
GraphiQL app from Github
{
viewer {
login
repositories (first:10) {
edges {
node {
name
}
}
}
}
Get only the data you want, strong typing, nested fields
With Django?
Graphene!
pip install graphene_django
INSTALLED_APPS += ['graphene_django']
urlpatterns += [url(r'^graphql', GraphQLView.as_view)]
Define nodes:
class TaskNode(DjangoObjectType):
class Meta:
model = Task
Whenever you have a field you can have a resolver for it that defines how to get the field.
Define queries:
class Query(ObjectType):
class Meta:
model = Task
Define the Relay:
{
goals (first: 5, after: "cursor") {
name
progress
}
}
Pagination
class GoalNode(DjangoObjectType):
progress = graphene.Float(description='The average task progress')
pk = graphene.Int()
tasks = graphene.List(TaskNode)
@graphene.resolve_only_args
def resolve_pk(self):
return self.pk
@graphene.resolve_only_args
def resolve_tasks(self):
retrun self.key_results.filter(closed=False)
class Meta:
model = Objective
filter_fields = ['name',]
exclude_fields = ['key_results',]
class OwnerNode(DjangoObjectType):
full_name = graphene.String()
GraphQL will display the description in the GraphiQL browser that’s useful!
Use cases that make it better than REST:
Complex views (dashboards, summaries, stats)
Complex Writes
Builders, autosaving, auto-updating. Checks the types! Define mutations!
Makes the change and then re-queries and returns the response in one call instead of many!
The backend knows what the mutation is and does it and the frontend just says what something should change to.
Make sure your mutations are @atomic!
Add it as a mutation on your schema.
Authorization
Performing authorization on each resolver is a pain in the ass, so you have to extend Graphene to authenticate on the connection.
Extend DjangoFilterConnectionField
connection_resolver: add user authentication
resolve_connection: get auth class from node and apply auth
Throttling/DOS
- Whitelist for allowed queries – all of the queries in the backend in python, and the frontend called the query by name. (def blah, return graph query)
- Maximum limit
- Maximum query cost – first: 50 repositories, first 10 issues = 550 total nodes.
- Rate limiting based on query cost. Based on the umber of database connections you’re getting.
Performance
Built to work, not to be performant. Doesn’t use select_related or prefetch related. Needs to reduce the count() calls.
Data Loader (facebook’s answer to performance issues) – return a promise that analyses the data requested and reduces the requests made
goals (first: 0) {
totalCount
}
Do the count in the graph instead of in the front end.
Need to write queries in a smart way!
Resources
- graphql.org
- Zero to GraphQL (video)
- Intro to GraphQL (blog)