GraphQL 打招呼

介紹一下

在遇見 GraphQL 的時候最常被比較的就是 Restful API,畢竟 Rustful API 是目前的主流設計,此時我們就必須了解到什麼情況下使用 GraphQL 能帶來效益

  • Over Fetch Restful API 的回傳會是一整個 resource, /user/1 會回傳 id=1 的 User 的全部資料,然而在很多情況下我們不需要這麼多的資料,可能 我們僅僅只需要 user 的名字而已 在 GraphQL 你可以指定你所需要的欄位,大大的減少了傳輸無用資料的消耗
  • Under Fetch 也就是 n+1 問題,情景是 user 有很多的 friends,每個朋友都是一名 User,在 Restful 的設計中,每個 User resource 都是一支 API,想想就頭痛 QQ。因此會在後端客製化 API 做 eager loading,一次回傳這些資料 但到了 GraphQL 這完全不是問題,你可以向下定義你所需要的資料,並嵌入 User 中
    query {
        User(id: 1) {
            Name
            AvatarURL
            friends {
                ID
                Name
            }
        }
    }
    

在設計上,GraphQL 關注 明確的解釋查詢語言與型態系統 而不去描述伺服器端實作,前後端之間以 Schema 定義溝通介面,雙方有共通的資料型態以及明確定義的詞彙來討論
讓一切清楚且簡單~

怎麼用r

工具會用一點 - GraphQL Playground

認識一些 GraphQL Schema

GraphQL 定義自己的語法格式,他是個 SDL (Schema Define Language)

首先來定義一個 type 也就是基本的物件,基本型別有 Int Float String Boolean ID,也可以用 ! 來定義該欄位是否是 Nullable 的

type Student {
  id: Int!
  name: String!
  "可以寫註解說每個學生都有 sid"
  studentID: String!
  
  """
  多行註解說:
  每個學生都可以填一個電話
  """
  phone: Phone
  
  """
  每個人都可以發很多文
  """
  articles: [Article!]
}

type Phone {
  phoneNumber: String!
}

type Article {
  title: String!
  content: String!
}

基本型別不夠用嗎,我們來自定義一下,Date 然後這個型別的實作要伺服器端自己來喔

scalar Date

# 用法這樣
type Article2 {
  title: String!
  content: String!
  date: Date!
}

也可以定義枚舉類別(enumeration)

enum GradingStatus {
    INPROGRESS
    GRADUATED
}

type Student2 {
  gradingStatus: GradingStatus!
}

當你的物件可能是不同的結構時,你可以找 union

"""
union 定義 User 可能是 Student 也可能是 Teacher
"""
union User = Student | Teacher

或是 interface 也能滿足你的需求

interface User {
  id: ID!
  name: String!
  phone: Phone
}

type Student implements User {
  id: ID!
  name: String!
  phone: Phone
  
  # 你還可以定義更多更多...
}

type Teacher implements User {
  id: ID!
  name: String!
  phone: Phone
  
  # 你還可以定義更多更多...

}

query mutation 驚嘆號 (enum with default)

語法知道一下 (query, mutation, subscription)

有三種基本語法 query, mutation, subscription

query

可以簡單想像他是 RestfulAPI 中的 GET,用來拿各種資料

fragment 可以抽出常用的結構來簡化語法

query {
  me {
    ...UserBrief
  }
  
  # 可以一次定義多個 query
  user(name: "foo") {
    id
    ...UserBrief
  }
}

# fragment 可以幫助重用參數
fragment UserBrief on User {
  name
  email
}

union 有時候回傳的不一定是同一種物件,這時候可以這麼做

# 一個 people 可能是 Teacher 或是 Student,不同的 type 會回傳不同的內容
query people {
    ...on Teacher {
        teacherID
        name
    }
    ...on Student {
        studentID
        name
        grade
    }
}

# 等價的,你也可以寫成這樣
query people {
    ...pTeacher
    ...pStudent
}

fragment pTeacher on Teacher {
    teacherID
    name
}

fragment pStudent on Student {
    studentID
    name
    grade
}

mutation

可以簡單想像他是 RestfulAPI 中的 POST,用來變更資料

mutation {
  newUser(name: "foo" email: "[email protected]") {
    id # 回傳的 id
    ...UserBrief
  }
}

fragment UserBrief on User {
  name
  email
}

也可以用 $ 開頭寫點變數

mutation create($name: String $email: String) {
  newUser(name: $name email: $email) {
    id # 回傳的 id
    ...UserBrief
  }
}

fragment UserBrief on User {
  name
  email
}

subscription

建立 websocket 長連線來接收資料的變更,用法跟 query 很像,在資料變更時就收到訊息囉

subscription {
  me {
    name
    email
  }
}

Introspection 自我查詢

GraphQL 可以利用自我查詢來取得目前的 Schema

query ListTypes {
  __schema {
    # 取得 gql 定義了哪些 Types
    types {
      kind # Object, InputObject, Enum, Scalar
      name
      description
      
      # 這裡也可以直接 fields 拿詳細資訊
    }
  }
}

query TypeDetail {
  # 我們想要拿 Role 這個 Type 的詳細資料
  __type(name: "Role") {
    name
    kind
    fields {
      name
      type { ofType{ name } }
    }
  }
}
query IntrospectionQuery {
  # 我要拿全部操作(query, mutation, subscription)
  __schema {
    queryType {
      ...OprationDetail
    }
    mutationType {
      ...OprationDetail
    }
    subscriptionType {
      ...OprationDetail
    }
  }
}

fragment OprationDetail on __Type {
  name
  fields { 
    name 
    type { ofType{ name } }
  }
}

graphQL 自帶文件,具備一般 API Server 沒有的優勢,棒棒ㄉ

HASURA

https://cloud.hasura.io/project/88f267cd-3f1b-45fa-a1e1-a7ceed6ba10a/env-vars

Ref

Think in GraphQL https://ithelp.ithome.com.tw/users/20111997/ironman/1878 https://graphql.org/learn/schema/