Slide 1

Slide 1 text

Overview of
 GraphQL & Clients ! # @zetavg
 fb.me/pokaichang72

Slide 2

Slide 2 text

My Background ⬢ Building stuff as a web developer from 2012 ⬢ Shallow experiences covered from design, mobile, front-end and backend develop to cloud deployments (AWS) ⬢ Fan of GraphQL/Relay of its beauty of API design since 2015 ⬢ Working at with Ruby, JavaScript (React.js) and playing Elixir ⬢ Former tech lead at Colorgy ! # @zetavg
 fb.me/pokaichang72

Slide 3

Slide 3 text

⬡ Complain about RESTful Introduce GraphQL ⬡ Just enough GraphQL to get started ⬡ GraphQL client library overview ⬡ Intro to Relay ⬡ Demo: GraphQL & Relay on Rails
 https:/ /github.com/zetavg/RailsRelayTodoMVC Outline

Slide 4

Slide 4 text

Background of API Developing

Slide 5

Slide 5 text

The evolution of API ⬢ RESTful: Easy to use, easy to develop ⬡ Directly based on Wide World Web ⬡ URI as resource name (noun), HTTP method as action (verb) ⬡ We need documents: Swagger... ⬡ ...and type definitions: JSON Schema ⬡ ...and data relations: JSON API ⬡ Combine them all: API Blueprint, RAML

Slide 6

Slide 6 text

The evolution of API ⬢ But for the front-end, especially SPA or mobile apps: ⬡ Querying complex data efficiently is still hard ⬡ We may come up with lots of endpoint versions ⬡ Or messy features on different endpoints ⬡ On purpose specs are hard to follow, without an clear interface, APIs tends to be hard to reuse and maintain ⬡ Writing code to fetch and store data is annoying ⬡ Caching is hard cause there's no explicit schema ⬡ Co-working may be messy cause there's no schema

Slide 7

Slide 7 text

/api/v1/posts.json /api/v2/posts.json /api/v3/posts.json /api/v4/posts.json /api/v65535/posts.json ⋯⋯

Slide 8

Slide 8 text

/api/posts.json /api/posts.json?include=author /api/posts.json?include=author,comments /api/posts.json?cover=true&include=author,comments /api/posts.json?cover=true&include=author,comments_

Slide 9

Slide 9 text

API should be like this

Slide 10

Slide 10 text

Not this

Slide 11

Slide 11 text

GraphQL ⬢ A new query language ⬢ Brief History: ⬡ 2012 - Used for Facebook mobile app ⬡ 2015 - Publicly released ⬡ 2017 - Now: GraphQL & Relay re-licensed under 
     MIT ⬢ Normally uses a single endpoint URL ( POST /graphql )

Slide 12

Slide 12 text

A Glance on GraphQL

Slide 13

Slide 13 text

All your application data 
 can be represented as a graph

Slide 14

Slide 14 text

$ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 } $ { "name": "Lucy", "bio":"...", "followers": [◌], "repos": [◌, ◌]
 } $ { "name": "Ja "bio":"..." "followers" "repos": [◌ } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], "repos": [◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Hello World", "description": "...", "stargazers": [◌, ◌]
 } ! { "name": "Handy Ut "description": ". "stargazers": [◌] } ! { "name": "Awes "description" "stargazers": } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } ! { } { "viewer": ◌
 }

Slide 15

Slide 15 text

A subset of the graph 
 is used to show an UI

Slide 16

Slide 16 text

$ { } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], "repos": [◌]
 } $ { "name": "Lucy", "bio":"...", "followers": [◌] "repos": [◌, ◌]
 } ! { "n "d "s } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Thing Compile "description": "...", "stargazers": [◌, ◌, ◌ } ! { "name" "descr "starg } { } { "viewer": ◌
 } $ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 } $ d d d d d d d d

Slide 17

Slide 17 text

! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Thing Compile "description": "...", "stargazers": [◌, ◌, ◌ } ! { "name" "descr "starg } ! { "n "d "s } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } { } { "viewer": ◌
 } $ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 } $ { "name": "Lucy", "bio":"...", "followers": [◌] "repos": [◌, ◌]
 } $ { } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], "repos": [◌]
 } $ $ $ $ $ $

Slide 18

Slide 18 text

! { "name" "descr "starg } ! { "name": "Thing Compile "description": "...", "stargazers": [◌, ◌, ◌ } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], "repos": [◌]
 } ! { "n "d "s } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } { } { "viewer": ◌
 } $ $ $ $ $ $ $ $ $ $ $ / $ { "name": "Lucy", "bio":"...", "followers": [◌] "repos": [◌, ◌]
 } $ { } $ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 }

Slide 19

Slide 19 text

$ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 } $ { "name": "Lucy", "bio":"...", "followers": [◌], "repos": [◌, ◌]
 } $ { "name": "Ja "bio":"..." "followers" "repos": [◌ } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], "repos": [◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Hello World", "description": "...", "stargazers": [◌, ◌]
 } ! { "name": "Handy Ut "description": ". "stargazers": [◌] } ! { "name": "Awes "description" "stargazers": } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } ! { } { "viewer": ◌
 } GraphQL

Slide 20

Slide 20 text

⬡ Complain about RESTful Introduce GraphQL ⬡ Just enough GraphQL to get started ⬡ GraphQL client library overview ⬡ Intro to Relay ⬡ Demo: GraphQL & Relay on Rails
 https:/ /github.com/zetavg/RailsRelayTodoMVC Outline

Slide 21

Slide 21 text

Basic Query ⬢ Starts with selecting fields on the query root ⬢ WYSIWYG { "data": { "viewer": { "name": "Pokai Chang" } } } query { viewer { name } }

Slide 22

Slide 22 text

Basic Query ⬢ Querying nested fields { "data": { "viewer": { "name": "Pokai Chang", "birthday": { "month": 7, "day": 2 } } } } query { viewer { name birthday { month day } } }

Slide 23

Slide 23 text

Types ⬢ Get the type of a object using the __typename meta field { "data": { "viewer": { "__typename": "User", "birthday": { "__typename": "Date" } } } } query { viewer { __typename birthday { __typename } } }

Slide 24

Slide 24 text

Type defs as docs # GraphQL query language
 
 query { viewer { name birthday { month day } following { name } } } # GraphQL schema language
 
 type Query { viewer: User } type User { name: String!
 birthday: Date
 followers: [User] following: [User] } type Date { year: Integer month: Integer day: Integer }

Slide 25

Slide 25 text

Non-Null & Lists # GraphQL schema language
 
 type Query { viewer: User } type User { name: String!
 birthday: Date
 followers: [User] following: [User] } type Date { year: Integer month: Integer day: Integer } [] means an array of objects ! means that the field is non-nullable

Slide 26

Slide 26 text

Arguments ⬢ Arguments can be defined on fields query { user(id: 1) { name } }

Slide 27

Slide 27 text

Arguments ⬢ Nested fields also can have arguments query { user(id: 1) { name repo(name: "awesome-graphql") { name description } } }

Slide 28

Slide 28 text

Variables ⬢ A way to dynamically change arguments for fields query ($userId: Int!, $repoName:String!) { user(id: $userId) { name repo(name: $repoName) { name description } } } {
 "userId": 1, "repoName": "awesome-graphql" } +

Slide 29

Slide 29 text

Fragment fragment profileFields on User { name bio avatarUrl } query { viewer { ...profileFields } user(id: 1) { ...profileFields } } Pre-define a set of fields
 on a type or interface as meaningful fragment

Slide 30

Slide 30 text

Interfaces ⬢ An abstract type that includes a set of fields that a type must define to implement ⬢ Can be used for fragments interface Actor { id: ID! name: String! avatarUrl: String! } type User implements Actor { id: ID! name: String! avatarUrl: String! ... } type Bot implements Actor { id: ID! # Sample Query fragment actorFields on Actor { name bio avatarUrl } query { feed { actor { ...actorFields }
 verb


Slide 31

Slide 31 text

Mutate Data w/ Mutations ⬢ Mutation queries lives under mutation instead of
 query , and are ways how we can change the data ⬢ We can put the input data in arguments, changed nodes will be returned in the selectable payload ⬢ It’s a convention like RESTful GET/POST that clients rely on mutation { addComment(input: { subjectId: 1, body: "Hi." }) { subject { comments { body } } } }

Slide 32

Slide 32 text

Input Types ⬢ Yes, inputs are also typed input AddCommentInput { subjectId: ID! body: String! } mutation { addComment(input: { subjectId: 1, body: "Hi." }) { subject { comments { body } } } }

Slide 33

Slide 33 text

GraphiQL ⬢ An open source GraphQL playground

Slide 34

Slide 34 text

Query tree ⬢ Each query is a tree extracted from the graph ⬢ The query is resolved by traversing the tree and resolving each field query { viewer { name
 bio repos { name
 description } } }

Slide 35

Slide 35 text

$ { "name": "J "bio":"... "followers "repos": [ } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], $ { "name": "Lucy", "bio":"...", "followers": [◌], "repos": [◌, ◌]
 } ! { "name": "Awe "description "stargazers" } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } ! ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Handy Ut "description": ". "stargazers": [◌] { } { "viewer": ◌
 } $ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 } Query tree

Slide 36

Slide 36 text

Query tree ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Handy Util", "description": "...", "stargazers": [◌]
 } { } { "viewer": ◌
 } $ { "name": "Pokai Chang", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 }

Slide 37

Slide 37 text

graphql.org

Slide 38

Slide 38 text

⬡ Complain about RESTful Introduce GraphQL ⬡ Just enough GraphQL to get started ⬡ GraphQL client library overview ⬡ Intro to Relay ⬡ Demo: GraphQL & Relay on Rails
 https:/ /github.com/zetavg/RailsRelayTodoMVC Outline

Slide 39

Slide 39 text

Fetching Pagination
 Caching Update Optimistic Update Realtime UI

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Fetching

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

const View = (data) => UI

Slide 44

Slide 44 text

Redux data flow View State subscribe Redux Store

Slide 45

Slide 45 text

Redux data flow View State Reducer Action subscribe prevState

Slide 46

Slide 46 text

Redux data flow View State Reducer Action subscribe prevState Backend ?

Slide 47

Slide 47 text

Redux data flow View State Reducer Action subscribe prevState Backend Action Action Action

Slide 48

Slide 48 text

$ { "name": "Neson", "bio":"Yet another geek.", "followers": [◌, ◌, ◌], "repos": [◌, ◌, ◌]
 } $ { "name": "Lucy", "bio":"...", "followers": [◌], "repos": [◌, ◌]
 } $ { "name": "Ja "bio":"..." "followers" "repos": [◌ } $ { "name": "Pusheen", "bio":"Nyan nyan nyan~", "followers": [◌], "repos": [◌]
 } ! { "name": "Thing Compiler", "description": "...", "stargazers": [◌, ◌, ◌]
 } ! { "name": "Hello World", "description": "...", "stargazers": [◌, ◌]
 } ! { "name": "Handy Ut "description": ". "stargazers": [◌] } ! { "name": "Awes "description" "stargazers": } ! { "name": "Todo", "description": "...", "stargazers": [◌]
 } ! { } { "viewer": ◌
 } GraphQL

Slide 49

Slide 49 text

Relay data fetching View $ $ $ $ $ d d $ $ $ $ d d d d d d Relay Store

Slide 50

Slide 50 text

Relay data fetching viewer { name bio } View d d d d d d d d viewer { repos { name description } } $ Relay Store

Slide 51

Slide 51 text

Relay data fetching View d d d d d d d d viewer { repos { name description } } Backend query { viewer {
 name
 bio repos { name description } } } $ Relay Store viewer { name bio }

Slide 52

Slide 52 text

Relay data fetching View d d d d d d d d viewer { repos { name description } } Backend { "data": { "viewer": {
 "name": "…", "bio": "…", "followers": […], "repos": […] } } } $ viewer { name bio } query { viewer {
 name
 bio repos { name description } } }

Slide 53

Slide 53 text

Relay data fetching View $ ㄎ ㄎ ㄎ ㄎ viewer { repos { name description } } d d d d d d d d viewer { name bio }

Slide 54

Slide 54 text

View Relay data fetching viewer { name bio } View $ ㄎ ㄎ ㄎ ㄎ viewer { following { name } } d d d d d d d d $ $ $ $ $

Slide 55

Slide 55 text

View Relay data fetching viewer { name bio } View $ ㄎ ㄎ ㄎ ㄎ viewer { following { name } } d d d d d d d d $ $ $ $ $ Backend query { viewer { following { name } } } { "data": { "viewer": { "following": […] } } }

Slide 56

Slide 56 text

View Relay data fetching viewer { name bio } View $ ㄎ ㄎ ㄎ ㄎ viewer { following { name } } d d d d d d d d $ $ $ $ $ ㄎ ㄎ ㄎ ㄎ

Slide 57

Slide 57 text

Caching

Slide 58

Slide 58 text

Query tree query { user(login: "zetavg") { name repositories { name } } } { "data": { "user": { "name": "Pokai Chang", "repositories": [ { "name": "dotfiles" }, { "name": "Thing" }, { "name": "Stuff" } ] } } }

Slide 59

Slide 59 text

Query tree Query Root User Repo Repo Repo name user(login: "zetavg") "Pokai Chang" "dotfiles" name repositories name "Thing" name "Stuff" query { user(login: "zetavg") { name repositories { name } } } { "data": { "user": { "name": "Pokai Chang", "repositories": [ { "name": "dotfiles" }, { "name": "Thing" }, { "name": "Stuff" } ] } } }

Slide 60

Slide 60 text

Caching the query result ⬢ Strategy 1: traverse path Query Root user(login: "zetavg") User Repo Repo Repo name "Pokai Chang" "dotfiles" name repos name "Thing" name "Stuff" ⬡ Same path, same object

Slide 61

Slide 61 text

⬢ Strategy 1: traverse path Query Root user(login: "zetavg") User Repo Repo Repo name "Pokai Chang" "dotfiles" name repos name "Thing" name "Stuff" user(login: "zetavg") user(login: "zetavg")/repos[2] ⬡ Same path, same object Caching the query result

Slide 62

Slide 62 text

⬢ Strategy 1: traverse path Query Root user(login: "zetavg") User Repo Repo Repo name "Pokai Chang" "dotfiles" name repos name "Thing" name "Stuff" repo(owner: "zetavg", name: "dotfiles") Repo name "dotfiles" ⬡ Sometimes path assumption isn’t enough Caching the query result

Slide 63

Slide 63 text

⬢ Strategy 1: traverse path Query Root user(login: "zetavg") User Repo Repo Repo name "Pokai Chang" "dotfiles" name repos name "Thing" name "Stuff" repo(owner: "zetavg", name: "dotfiles") Repo name "dotfiles" Same object on different path ⬡ Sometimes path assumption isn’t enough Caching the query result

Slide 64

Slide 64 text

⬢ Strategy 1: traverse path ⬢ Strategy 0: object identifier repo/dotfiles Repo name "dotfiles" repo/dotfiles Repo name "dotfiles" Query Root User Repo Repo user(login: "zetavg") "Pokai Chang" name repos name "Thing" name "Stuff" repo(owner: "zetavg", name: "dotfiles") repo/Thing repo/Stuff Caching the query result

Slide 65

Slide 65 text

⬢ Strategy 1: traverse path ⬢ Strategy 0: object identifier Query Root User Repo Repo user(login: "zetavg") "Pokai Chang" name repos name "Thing" name "Stuff" repo(owner: "zetavg", name: "dotfiles") repo/Thing repo/Stuff Repo name "dotfiles" repo/dotfiles Caching the query result

Slide 66

Slide 66 text

⬢ Strategy 1: traverse path ⬢ Strategy 0: object identifier ⬡ Relay: we need the server to give a global id for nodes that need to be identified ⬡ Apollo: client defines a dataIdFromObject function that will be executed on every node ⬡ Fun fact: Relay stores each object it fetched in a key-value store with the object id or traverse path as key, any field that contains an object will actually be the key of the object, so two objects having the same id will be ensured the same by Implementation Caching the query result

Slide 67

Slide 67 text

Pagination

Slide 68

Slide 68 text

Cursor Based Pagination

Slide 69

Slide 69 text

⬢ Offset based pagination, e.g.: per_page=5&page=1 Cursor? page 1 page 2 page 3

Slide 70

Slide 70 text

⬢ Offset based pagination, e.g.: per_page=5&page=1 ⬢ Cursor? page 1 page 2 page 3 1 2 3 4 5 page 1 Client fetches page 1

Slide 71

Slide 71 text

⬢ Offset based pagination, e.g.: per_page=5&page=1 ⬢ Cursor? page 1 page 2 page 3 ' Broken page 1 page 2 page 3 1 2 3 4 5 page 1 Data inserted

Slide 72

Slide 72 text

⬢ Offset based pagination, e.g.: per_page=5&page=1 ⬢ Cursor? page 1 page 2 page 3 ' Broken page 1 page 2 page 3 1 2 3 4 5 page 1 page 2 5 6 7 8 9 ' Client got malformed results

Slide 73

Slide 73 text

⬢ Offset based pagination, e.g.: per_page=5&page=1 ⬢ ⬢ Cursor based pagination, e.g.: after: "…", next: 5 Cursor? next 5 next 5 page 1 page 2 page 3 ' Broken page 1 page 2 page 3

Slide 74

Slide 74 text

Relay Connections ⬢ The design of Relay Cursor Connections query { viewer { friends(first: 10, after: "someCursor") { edges { cursor node { id name } } pageInfo { hasNextPage } } } } Edge (UserEdgeType) Node (UserType) { … } Cursor Current cursor Connection Edges Edge (UserEdgeType) Node (UserType) { … } Cursor Edge (UserEdgeTy Node (UserType { … } Cursor Page Info Starting cursor

Slide 75

Slide 75 text

Update

Slide 76

Slide 76 text

Mutations ⬢ A mutation is a query that has side effects ⬢ The changes made on the graph will be put on the response, the client is responsible to select the necessary parts mutation { renameRepo(input: { repoID: "…", name: "NewName" }) { repo {
 id name } } } Grab the changes that are
 made on the existing repo

Slide 77

Slide 77 text

Mutations ⬢ A mutation is a query that has side effects ⬢ The changes made on the graph will be put on the response, the client is responsible to select the necessary parts ⬢ In general, we need to write an updater function to update the store with the payload:
 
 (oldState, payload) => newState ⬢ Relay and Apollo both has some conventions ⬡ Objects with matching identifier in the store will be updated automatically

Slide 78

Slide 78 text

Mutations UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Store

Slide 79

Slide 79 text

Mutations UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Server Store

Slide 80

Slide 80 text

Mutations UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Server Updater Store Response

Slide 81

Slide 81 text

Mutations UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Server Updater Store Response

Slide 82

Slide 82 text

Optimistic Update

Slide 83

Slide 83 text

Mutations UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Server Updater Store Latency Response '

Slide 84

Slide 84 text

Optimistic Update UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Store Latency

Slide 85

Slide 85 text

Optimistic Update UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Optimistic Updater Store Latency Optimistic Update Layer

Slide 86

Slide 86 text

Optimistic Update UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Optimistic Updater Server Updater Store Latency Response Optimistic Update Layer

Slide 87

Slide 87 text

Optimistic Update UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Mutation Optimistic Updater Server Updater Store Latency Response

Slide 88

Slide 88 text

Realtime UI

Slide 89

Slide 89 text

GraphQL Live Query ⬢ Idea: after the client sends a query, server can push updates of the query result to the client ⬢ May require a fully reactive backend ⬢ No open implementations yet

Slide 90

Slide 90 text

GraphQL Subscriptions ⬢ Clients can subscribe to a specific type of event as a similar way as how we do mutations ⬢ Mutations are client-made changes while Subscriptions are server-pushed updates ⬢ New query results will be pushed to the client when a event occurred subscription { todoItemAddedToList(todoListID: "…") { todoItem { name } } }

Slide 91

Slide 91 text

GraphQL Subscriptions ⬢ Clients can subscribe to a specific type of event as a similar way as how we do mutations ⬢ Mutations are client-made changes while Subscriptions are server-pushed updates ⬢ New query results will be pushed to the client when a event occurred ⬢ GraphQL just tells us how things should work, we need to configure different implementations (WebSocket, APNS, GCM) of sending the data on different platforms

Slide 92

Slide 92 text

GraphQL Subscriptions UI $ ㄎ ㄎ ㄎ ㄎ d d d d d d Subscription Server Updater Store When event occurred On mount (normally)

Slide 93

Slide 93 text

References ⬢ GraphQL API Explorer ⬢ GraphQL Concepts Visualized ⬢ Mutations and Optimistic UI in Apollo Client ⬢ GraphQL Subscriptions in Apollo Client ⬢ https://github.com/zetavg/graphql-todomvc

Slide 94

Slide 94 text

Thanks + Q&A