GraphQL

overview

WHo am I

  • 周杰 Jay Chou
  • 約莫兩年 Web Developer 的 經驗
  • 目前在 Yoctol.AI 打工
  • Introduction(How to use, how to design API)
  • Props and Cons
  • Ecosystem(Apollo, GraphQL CDN)
  • Typescript issue

Intro

  • In 2012, From Facebook
  • Query Language for API

Intro

How to use

Query

{
  "data": {
    "human": {
      "id": 1000,
      "name": "Luke Skywalker",
      "height": 172
    }
  }
}

Response

query {
  findHuman {
    human(id: "1000") {
      id
      name
      height
    }
  }
}

Request

(GET )

Mutation

Request

mutation CreateHuman($name: String!, $height: Int!) {
  createHuman(name: $name, height: $height) {
    id
    name
    height
  }
}

Variables

{
  "name": "Jay Chou",
  "height": 174,
}

(UPDATE)

Mutation

Request

mutation CreateHuman($name: String!, $height: Int!) {
  createHuman(name: $name, height: $height) {
    id
    name
    height
  }
}

Variables

{
  "name": "Jay Chou",
  "height": 174,
}
{
  "data": {
    "human": {
      "id": 1001,
      "name": "Jay Chou",
      "height": 174
    }
  }
}

Response

(UPDATE)

Mutation

Request

mutation CreateHuman($name: String!, $height: Int!) {
  createHuman(name: $name, height: $height) {
    id
    name
    height
  }
}

Variables

{
  "name": "Jay Chou",
  "height": 174,
}
{
  "data": {
    "human": {
      "id": 1001,
      "name": "Jay Chou",
      "height": 174
    }
  }
}

Response

(UPDATE)

Subscription/Fragment/Directive......etc

GraphQL Doc

under the hood

src: https://chanakaudaya.medium.com/graphql-based-solution-architecture-patterns-8905de6ff87e

query {
  findHuman {
    human(id: "1000") {
      id
      name
      height
    }
  }
}

gql

under the hood

src: https://chanakaudaya.medium.com/graphql-based-solution-architecture-patterns-8905de6ff87e

query {
  findHuman {
    human(id: "1000") {
      id
      name
      height
    }
  }
}

gql

under the hood

gql

1. write gql with the corresponding library

under the hood

gql

/graphql

2. send request via a single URL/endpoint

under the hood

gql

3. resolve gql, get/update data with the corresponding library

under the hood

Post method with JSON Payload

HOW TO Design SCHEMA/API

define Object/write Resolvers

HOW TO Design SCHEMA/API

type Human {
  id: ID!
  name: String!
  height: Int!
  clubs: [Club]
}

type Club {
  id: ID!
  name: String!
}

Object Type

Int
Float
String
Boolean
ID

(Default) Scalar Types

(Custom) Scalar Types

scalar Date

just like define ORM entity to some degree 🤔

HOW TO Design SCHEMA/API

Query: {
  human(obj, args, context, info) {
    return context.db.loadHumanByID(args.id).then(
      userData => new Human(userData)
    )
  }
}

This example is written in JavaScript, however GraphQL servers can be built in many different languages.

Resolver

Architecture

src: https://chanakaudaya.medium.com/graphql-based-solution-architecture-patterns-8905de6ff87e

Architecture

src: https://chanakaudaya.medium.com/graphql-based-solution-architecture-patterns-8905de6ff87e

Architecture

Props & Cons

Props in my opinion

  • Auto Documentation(Introspection)
  • Better DX
  • Reduce Bandwidth
  • Strictly-typed interfaces

Auto Documentation

(Introspection)

{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Query"
        },
        {
          "name": "String"
        },
        {
          "name": "ID"
        },
        {
          "name": "Mutation"
        },
        {
          "name": "Episode"
        },
        {
          "name": "Character"
        },
        {
          "name": "Int"
        },
        {
          "name": "LengthUnit"
        },
        {
          "name": "Human"
        },
        {
          "name": "Float"
        },
        {
          "name": "Droid"
        },
        {
          "name": "FriendsConnection"
        },
        {
          "name": "FriendsEdge"
        },
        {
          "name": "PageInfo"
        },
        {
          "name": "Boolean"
        },
        {
          "name": "Review"
        },
        {
          "name": "ReviewInput"
        },
        {
          "name": "Starship"
        },
        {
          "name": "SearchResult"
        },
        {
          "name": "__Schema"
        },
        {
          "name": "__Type"
        },
        {
          "name": "__TypeKind"
        },
        {
          "name": "__Field"
        },
        {
          "name": "__InputValue"
        },
        {
          "name": "__EnumValue"
        },
        {
          "name": "__Directive"
        },
        {
          "name": "__DirectiveLocation"
        }
      ]
    }
  }
}

Response

query {
  __schema {
    types {
      name
    }
  }
}

Request

BETTER dx

.gql easy to understand

query {
  findHuman {
    human(id: "1000") {
      id
      name
      height
      clubs {
      	id
        name
      }
    }
  }
}

nested data

BETTER dx

query {
  findHuman {
    human(id: "1000") {
      id
      name
      height
      clubs {
      	id
        name
      }
    }
  }
}

nested data

Less time spent documenting and navigating APIs

REDUCE BANDWIDTH

Let's talk about REST first

REDUCE BANDWIDTH

  • Over-fetching
  • Multiple requests for multiple resources
  • Waterfall network requests on nested data
  • Each client need to know the location of each service

In REST comes with some downsides like:

REDUCE BANDWIDTH

  • Over-fetching
  • Multiple requests for multiple resources
  • Waterfall network requests on nested data
  • Each client need to know the location of each service

In REST comes with some downsides like:

query {
  findHuman {
    human(id: "1000") {
      id
      name
      height
      clubs {
      	id
        name
      }
    }
    
    project (id: '123') {
      id
      name  
    }
  }
}

REST

https://www.howtographql.com/basics/1-graphql-is-the-better-rest

 multi request

GraphQL

https://www.howtographql.com/basics/1-graphql-is-the-better-rest

 single request

SINGLE ENDPOINT

No more Over- and Under-fetching

Over-fetching: Downloading superfluous data

Under-fetching and the n+1 problem

https://www.howtographql.com/basics/1-graphql-is-the-better-rest

Concern: Request JSON payload cost more bytes?

This is a trade off,  but here come some solution ......

We will explain later🤪

CONS in my opinion

  • Make your architecture more complicated
  • Http cache

Make your architecture more complicated

more code​/more library to learn/more bundle size/securitiy issue

 

If front-end dev not familiar with graphQL schema,

he/she will get useless field and cost performance

-> trigger fieldResolver

Make your architecture more complicated

more code​/more library to learn/more bundle size/securitiy issue

 

Make your architecture more complicated

more code​/more library to learn/more bundle size/securitiy issue

 

Introspection/Authorize issue

Http cache

By providing many endpoints, a REST API enables easy web caching configurations to match certain URL patterns, HTTP methods, or specific resources. 

Due to having only one endpoint with many different queries, it’s much harder to use this type of caching with a GraphQL API.

Lets talk more about cache

Apollo Tech-talk:  Simple caching, made difficult

Responses to [the POST] method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields

Change POST TO GET

You can't use GET since query parameters have size limits!

URI length issue

狠角色

Solution

Application/Network 

Application

CHECK URI LENGTH

export default function request(url, query, variables) {
  return fetch(`${url}?query=${query}&variables=${variables}`)
  	.then(result => {
      if (result.status !== 414) {
        return result;
      }
    
    return fetch(url, {
      method: 'POST',
      body: JSON.stingify({ query, variables })
    });
  })
}

Persisted Queries

  • use Get method to help CDN cache
  • hash payload, let cache easier
  • reduce the time of server resolve graphQL query

Apollo Persist Queries

Instagram

Apollo client

Apollo Client stores the results of your GraphQL queries in a local, normalized, in-memory cache.

 

This enables Apollo Client to respond almost immediately to queries for already-cached data, without even sending a network request.

manage API state

Apollo client Cache

Globally Unique ID:  _typename + id

{
  "__typename": "Person",
  "id": "cGVvcGxlOjE=",
  "name": "Luke Skywalker",

  "homeworld": {
    "__typename": "Planet",
    "id": "cGxhbmV0czox",
    "name": "Tatooine"
  }
}
{
  "__typename": "Person",
  "id": "cGVvcGxlOjE=",
  "name": "Luke Skywalker",

  "homeworld": {
    "__ref": "Planet:cGxhbmV0czox"
  }
}

Apollo client Cache

{
  "__typename": "Person",
  "id": "cGVvcGxlOjE=",
  "name": "Luke Skywalker",

  "homeworld": {
    "__typename": "Planet",
    "id": "cGxhbmV0czox",
    "name": "Tatooine"
  }
}
{
  "__typename": "Person",
  "id": "cGVvcGxlOjE=",
  "name": "Luke Skywalker",

  "homeworld": {
    "__ref": "Planet:cGxhbmV0czox"
  }
}

Globally Unique ID:  _typename + id

Apollo Server Cache

type Post {
  id: ID!
  title: String
  author: Author
  votes: Int @cacheControl(maxAge: 30)
  comments: [Comment]
  readByCurrentUser: Boolean! @cacheControl(maxAge: 10, scope: PRIVATE)
}

schema

Apollo Server Cache

Whenever Apollo Server sends an operation response that has a non-zero maxAge, it includes a Cache-Control HTTP header that describes the response's cache policy.

 

Cache-Control: max-age=60, private

HTTP header format

 But there are problems with these though.

Similar queries

query {
  food {
    title
  }
}
query {
  food {
    title
    body
  }
}
query {
  # with comments
  food {
    title
  }
}

generate different cache keys -> In reality

not smart enough 😢 

NETWORK

Another cache issue solution:  GraphQL CDN

Graphql CDN

creator of Styled Component

  • Advanced Edge Network
  • Per-user Caching
  • API Analytics & Insights
  • Malicious Query Detection

More Info -> https://graphcdn.io

REFERENCE

  • GraphQL Doc
  • Apollo GraphQL Doc
  • https://hackmd.io/C2yzLJJETLq3lNYM_cHzGw
  • https://hackmd.io/NG2Rm8IVSHy30nLSo_OG0A

One More thing

Valley22

投資了酒吧,在信義安和捷運站旁邊,歡迎大家蒞臨😎

GraphQL-Overview

By Jay Chou

GraphQL-Overview

  • 439