The document provides an overview of GraphQL and GraphQL clients. It discusses:
- The evolution of APIs from RESTful to GraphQL, which provides a more efficient way to query complex data.
- How GraphQL uses a single endpoint and allows clients to specify exactly the data they need through queries.
- Basic GraphQL queries, including selecting fields, nested fields, arguments, variables, fragments, and mutations.
- GraphQL type definitions that serve as documentation.
- GraphQL clients like Relay that optimize data fetching and caching.
- Tools like GraphiQL that allow testing GraphQL queries in an interactive environment.
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
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
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 )
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
}
}
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
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
}
}
}
}
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
}
}
}
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
50. Relay data fetching
viewer {
name
bio
}
View
dd
dd
dd
dd
viewer {
repos {
name
description
}
}
$
Relay Store
51. 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
}
52. 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
}
}
}
59. 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" }
]
}
}
}
60. 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
61. ⬢ 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
62. ⬢ 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
63. ⬢ 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
64. ⬢ 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
65. ⬢ 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
66. ⬢ 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
76. 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
77. 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
89. 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
90. 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
}
}
}
91. 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
93. 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