Advertisement
Advertisement

More Related Content

Advertisement
Advertisement

Overview of GraphQL & Clients

  1. Overview of
 GraphQL & Clients ! # @zetavg
 fb.me/pokaichang72
  2. 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
  3. ⬡ 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
  4. Background of API Developing
  5. 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
  6. 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
  7. /api/v1/posts.json /api/v2/posts.json /api/v3/posts.json /api/v4/posts.json /api/v65535/posts.json ⋯⋯
  8. /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_
  9. API should be like this
  10. Not this
  11. 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 )
  12. A Glance on GraphQL
  13. All your application data 
 can be represented as a graph
  14. $ { "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": ◌
 }
  15. A subset of the graph 
 is used to show an UI
  16. $ { } $ { "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": [◌, ◌, ◌]
 } $ dd dd dd dd
  17. ! { "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": [◌]
 } $ $ $ $ $ $
  18. ! { "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": [◌, ◌, ◌]
 }
  19. $ { "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
  20. ⬡ 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
  21. Basic Query ⬢ Starts with selecting fields on the query root ⬢ WYSIWYG { "data": { "viewer": { "name": "Pokai Chang" } } } query { viewer { name } }
  22. Basic Query ⬢ Querying nested fields { "data": { "viewer": { "name": "Pokai Chang", "birthday": { "month": 7, "day": 2 } } } } query { viewer { name birthday { month day } } }
  23. Types ⬢ Get the type of a object using the __typename meta field { "data": { "viewer": { "__typename": "User", "birthday": { "__typename": "Date" } } } } query { viewer { __typename birthday { __typename } } }
  24. 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 }
  25. 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 } [<thing>] means an array of <thing> objects ! means that the field is non-nullable
  26. Arguments ⬢ Arguments can be defined on fields query { user(id: 1) { name } }
  27. Arguments ⬢ Nested fields also can have arguments query { user(id: 1) { name repo(name: "awesome-graphql") { name description } } }
  28. 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" } +
  29. 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
  30. 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

  31. 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 } } } }
  32. Input Types ⬢ Yes, inputs are also typed input AddCommentInput { subjectId: ID! body: String! } mutation { addComment(input: { subjectId: 1, body: "Hi." }) { subject { comments { body } } } }
  33. GraphiQL ⬢ An open source GraphQL playground
  34. 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 } } }
  35. $ { "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
  36. 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": [◌, ◌, ◌]
 }
  37. graphql.org
  38. ⬡ 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
  39. Fetching Pagination
 Caching Update Optimistic Update Realtime UI
  40. Fetching
  41. const View = (data) => UI
  42. Redux data flow View State subscribe Redux Store
  43. Redux data flow View State Reducer Action subscribe prevState
  44. Redux data flow View State Reducer Action subscribe prevState Backend ?
  45. Redux data flow View State Reducer Action subscribe prevState Backend Action Action Action
  46. $ { "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
  47. Relay data fetching View $ $ $ $ $ dd $ $ $ $ dd dd dd Relay Store
  48. Relay data fetching viewer { name bio } View dd dd dd dd viewer { repos { name description } } $ Relay Store
  49. Relay data fetching View dd dd dd dd viewer { repos { name description } } Backend query { viewer {
 name
 bio repos { name description } } } $ Relay Store viewer { name bio }
  50. Relay data fetching View dd dd dd dd viewer { repos { name description } } Backend { "data": { "viewer": {
 "name": "…", "bio": "…", "followers": […], "repos": […] } } } $ viewer { name bio } query { viewer {
 name
 bio repos { name description } } }
  51. Relay data fetching View $ ㄎㄎㄎㄎ viewer { repos { name description } } dd dd dd dd viewer { name bio }
  52. View Relay data fetching viewer { name bio } View $ ㄎㄎㄎㄎ viewer { following { name } } dd dd dd dd $ $ $ $ $
  53. View Relay data fetching viewer { name bio } View $ ㄎㄎㄎㄎ viewer { following { name } } dd dd dd dd $ $ $ $ $ Backend query { viewer { following { name } } } { "data": { "viewer": { "following": […] } } }
  54. View Relay data fetching viewer { name bio } View $ ㄎㄎㄎㄎ viewer { following { name } } dd dd dd dd $ $ $ $ $ ㄎㄎㄎㄎ
  55. Caching
  56. Query tree query { user(login: "zetavg") { name repositories { name } } } { "data": { "user": { "name": "Pokai Chang", "repositories": [ { "name": "dotfiles" }, { "name": "Thing" }, { "name": "Stuff" } ] } } }
  57. 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" } ] } } }
  58. 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
  59. ⬢ 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
  60. ⬢ 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
  61. ⬢ 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
  62. ⬢ 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
  63. ⬢ 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
  64. ⬢ 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
  65. Pagination
  66. Cursor Based Pagination
  67. ⬢ Offset based pagination, e.g.: per_page=5&page=1 Cursor? page 1 page 2 page 3
  68. ⬢ 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
  69. ⬢ 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
  70. ⬢ 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
  71. ⬢ Offset based pagination, e.g.: per_page=5&page=1 ⬢ ⬢ Cursor based pagination, e.g.: after: "…", next: 5 Cursor? next 5next 5 page 1 page 2 page 3 ' Broken page 1 page 2 page 3
  72. 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
  73. Update
  74. 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
  75. 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
  76. Mutations UI $ ㄎㄎㄎㄎ dd dd dd Mutation Store
  77. Mutations UI $ ㄎㄎㄎㄎ dd dd dd Mutation Server Store
  78. Mutations UI $ ㄎㄎㄎㄎ dd dd dd Mutation Server Updater Store Response
  79. Mutations UI $ ㄎㄎㄎㄎ dd dd dd Mutation Server Updater Store Response
  80. Optimistic Update
  81. Mutations UI $ ㄎㄎㄎㄎ dd dd dd Mutation Server Updater Store Latency Response '
  82. Optimistic Update UI $ ㄎㄎㄎㄎ dd dd dd Mutation Store Latency
  83. Optimistic Update UI $ ㄎㄎㄎㄎ dd dd dd Mutation Optimistic Updater Store Latency Optimistic Update Layer
  84. Optimistic Update UI $ ㄎㄎㄎㄎ dd dd dd Mutation Optimistic Updater Server Updater Store Latency Response Optimistic Update Layer
  85. Optimistic Update UI $ ㄎㄎㄎㄎ dd dd dd Mutation Optimistic Updater Server Updater Store Latency Response
  86. Realtime UI
  87. 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
  88. 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 } } }
  89. 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
  90. GraphQL Subscriptions UI $ ㄎㄎㄎㄎ dd dd dd Subscription Server Updater Store When event occurred On mount (normally)
  91. 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
  92. Thanks + Q&A
Advertisement