Win back lovely API - GraphQL in Python

Keith Yang, May 2018, PyCon

Win back lovely API
GraphQL in Python

Keith Yang

Web and utilities in Python since 2008

Senior developer in iCHEF F&B POS

Taipei.py Co-founder

PyCon Taiwan voluteer since 2012

Chairpersion of PyCon APAC 2015

<< Ready Player One >>

Content Structure

from future import __hope__

These slides are and are not

iCHEF: Point of Sale platform

At the point of sale, the merchant calculates the amount owed by the customer, indicates that amount, may prepare an invoice for the customer, which may be a cash register printout, and indicates the options for the customer to make payment. -- Point of Sale, Wikipedia

iCHEF R&D Teams

iCHEF machine

Some architecture and deployment in iCHEF

Question 1
what's and where to find
current go-to API implement?

Existed APIs in varieties

Disclaimer: not all caused by RESTful if carefully crafted with OpenAPI Specification (Swagger or Stoplight)

© 2014? 2015, 2016, 2017, 2018

Question 2
what type of fields
will be in response?

No idea until response back

HTTP GET

/games/1
{
    'status': 123,
    'name': '🐲'
    },
}

Question 3
any
docuement?





  1. No document
  2. Somewhere outdated document
  3. Code is Law (like 1?)

What we looking for

  1. Consistent interface: flexible and predictable
    • Especially when it's time to open our API to third party
  2. Doable ans scalable: step by step, team by team
  3. Future promising: inspiring and fun

Trend of GraphQL vs REST API vs Python programming: the rise of GraphQL and REST API

🍰 ☕️ 🍕 🍺
LIFE EASIER
in short 🐛

GraphQL
glances of

Look of GraphQL request

HTTP GET
/graphql?query={ restaurant(id: "1") \
    { name, category { primary } } }
{
    'data': {
        'category': {
            'primary': 'Taiwanese Cuisine',
        },
        'name': 'Mazendo'
    },
    'errors': []
}

A sample of REST request

HTTP GET

/restaurants/1
{
    'category': {
        'primary': 'Taiwanese Cuisine',
        'second': 'Beef Noodle',
    },
    'name': 'Mazendo'
    },
}

GraphQL vs REST:
Similar Parts

GraphQL vs REST:
Different Parts

GraphQL vs REST:
Different Parts (cont.)

GraphQL vs REST:
Different Parts (cont.)

GET or POST
HTTP 200

Language agnostic
3-in-1 idea in a GraphQL request

For example, between ECMAScript and Python

Versioning Free
bug 🐛 feature

Versioning Free (cont.)

The shape of the returned data is determined entirely by the client's query, so servers become simpler and easy to generalize. When you're adding new product features, additional fields can be added to the server, leaving existing clients unaffected.

GraphQL License:
Open Web Foundation Agreement (OWFa) v1.0

(...) designed for collaborative open standards and supported by other well-known companies. The OWFa allows GraphQL to be implemented under a royalty-free basis, and allows other organizations to contribute to the project on reasonable terms.
-- Relicensing the GraphQL specification by Lee Byron, Sep 27, 2017

All kinds of platform for GraphQL

GraphQL easy benefits

Development with GraphQL?

  1. Schema
  2. UI and API
  3. Product

Real development with GraphQL

  1. Schema
  2. UI and API
  3. Product

Product design

UI design

query {
    restaurant {
        logoUrl: String!
    }
}

API desgin

♻️ Schema design

Naming
still hard

Naming hazard of fields

Sometimes it's harder for people whose English not first language to get it right

GraphQL Schema

GraphQL query schema sample

query {
    restaurant {
        settings {
            ...
        }
    }
}

Common error message format:
errors and data

{
    "errors": [{
        "message": String,
        ...
    }],
    "data": {
        "restaurant": {
            "settings": {...}
        }
    }
}

GraphQL schemas and types

Where it takes time to learn
Grow with spec evolving

What GraphQL type language like

interface Restaurant {
    id: ID!
    name: String!
    categories: [Category]!
}
type TaiwaneseRestaurant implements Restaurant {
    id: ID!
    name: String!
    categories: [Category]!
    tax_unique_identifier: String
}

GraphQL type language

pip install graphene

GroupCreated with Sketch.

GroupCreated with Sketch.

Graphene v2.1.2 Coverage: 96%

A piece of spec
implemented with Graphene

import graphene
...
class ReportObject(graphene.ObjectType):
    timestamp = graphene.Int()
    uuid = graphene.UUID()
    type = InventoryTypeGrapheneEnum()
    ...
    purchase_amount = graphene.String()
    counting_amount = graphene.Float()

GraphQL in iCHEF !=

GraphQL in Yelp

GraphQL in Cousera

GraphQL in IBM

GraphQL in Khan Academy

GraphQL in iCHEF

How to prompt the idea in the company?

Mock

Django serializer

does work together with graphene-django

Quick graphene-django example

cookbook/urls.py
from django.conf.urls import url
from django.contrib import admin

from graphene_django.views import GraphQLView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^graphql', GraphQLView.as_view(graphiql=True)),
]

graphene-django example (cont.)

cookbook/recipes/schema.py
from cookbook.recipes.models import Recipe, RecipeIngredient
from graphene import Node
from graphene_django.types import DjangoObjectType

class RecipeNode(DjangoObjectType):
    class Meta:
        model = Recipe
        interfaces = (Node, )
        filter_fields = ['title','amounts']

graphene-django example (cont.)

cookbook/recipes/schema.py
from graphene_django.filter import DjangoFilterConnectionField
class Query(object):
    recipe = Node.Field(RecipeNode)
    all_recipes = DjangoFilterConnectionField(RecipeNode)

    recipeingredient = Node.Field(RecipeIngredientNode)
    all_recipeingredients = DjangoFilterConnectionField(
        RecipeIngredientNode)

Integration with DRF

It does provide SerializerMutation to help you with DRF. However, in some cases customerized serializer mutation is preferred due to the varied product/API spec.

from graphene_django.rest_framework.mutation import \
    SerializerMutation
class MyAwesomeMutation(SerializerMutation):
    class Meta:
        serializer_class = MySerializer

Client Authentication

type Mutation {  #The new mutation
    createUser(name: String!, authProvider: AuthData!): User
    createLink(url: String!, description: String!): Link
}
type User {
    id: ID!
    name: String!
    email: String
    password: String
}
input AuthData {
    email: String!
    password: String!
}

Numeric detail

ichef/queryfilter

To share same query interface between Django ORM, SQLAlchemy, GraphQL backend, and raw dict

Example of queryfilter

dicts = [
    {"age": None},
    {"age": 1},
    {"age": 2},
]
query_filter = QueryFilter({
    "age": {
        "type": "number",
        "options": {"drop_none": True},
        "min": 0, "max": 1,
    }
})
assert len(query_filter.on_dicts(dicts)) == 1

Example of queryfilter

import graphene
from queryfilter import (QueryFilter,
    BirthFilterQueryType, TextFilterQueryType,
    NumberRangeFilterQueryType)
 
class UserQueryFilter(graphene.InputObjectType):
    name = TextFilterQueryType()
    birth = BirthFilterQueryType()
    total_login = NumberRangeFilterQueryType()

Summary

GraphQL is evolving
even production ready

<< New Release of GraphQL blah >>

I’ve spent 2 years trying to get Apollo Server into prod,
now I’ll no doubt have to make changes.

References

TODO

Q&A

Q&A 1:
Is it worth to migrate existed APIs?

Q&A 2:
Then HOW?

Strategies
Plans
Fun

Start small and stay finish

Motivation

Communication

Just like all better technology you hope to have in the orgazination, it's more of team and culture. Then it's not always someone's fault, in different conditions, it's just not convincing. Try listen. But if you didn't try, things surely won't happen. Sometimes it's just that the technology not worthy indead. For this one, I encourage you to try on, as it is happening around the world.

Thank you