學習 GraphQL 筆記(一)Object Type

Search for a command to run...

No comments yet. Be the first to comment.
前面學完了怎麼請求資料,怎麼定義 Schema,最後我們就是要來學習怎麼解析請求來拿取資料。 此處我是搭配 Apollo 來使用,所以可能跟其他 Framework 不同。到了 Resolver 基本上我認為就已經不關 GraphQL 的事情了,就主要是後端的邏輯該怎麼利用得到的參數來從 Database 取得資料或更新資料。 Operation 前面提到 GraphQL 的 Operation 是 Entry Point,所以 Front End 請求所用的都需要在 Resolver 也定義,...
現在應該開始有很多人想要踏入軟體工程的領域,但在進入這個領域之前,覺得先了解一些名詞,可以在入門時更有方向也更知道要用什麼關鍵字去找尋有用的資訊。這篇文章就是想要幫助想要入門的人理解一些軟體工程裡的專有名詞。 作業系統 這一區塊主要解釋跟作業系統層面相關的名詞 英文中文解釋 Operation system 簡稱 OS | 作業系統 | 就是電腦的作業系統,是三大作業系統分別是:Linux、Windows、macOS | | Linux | | 自由和開放原始碼的 UNI...
現在開始因為 ChatGPT 的出現,各種 AI 助手的功能都跑出來了。想想自己用了許久的環境設定也應該要來重新審視和建立新的開發環境了,僅此紀錄我個人的環境配置步驟和設定。 環境前置步驟 還原 MacBook Pro 至全新環境 macOS(全部資料刪除) 設定好初始設定後,登入 Apple ID 進入 App Store 確定 macOS 版本和預設 APP 都更新到最新 macOS 版本 到系統設定調整所有設定至個人習慣的設定 三指拖移 觸控板手勢開啟 防火牆開啟 輸入法設定...
從 ChatGPT 問世到現在,有許許多多的文章和討論出來。先從最早的 Google 要完蛋了,到後來的工作要被取代了,工程師失業了。 我就比較沒有想要馬上出來評論一下,我喜歡讓子彈飛一會兒。跟討論一下我自己比較在意的討論點。 Google 為什麼慢了? 結論:因為他需要更小心 很多人說 Google 怎麼被微軟搶先了一步。剛開始 Bing 說要加上 AI 的時候大家都在說 Google 怎麼慢了。我就馬上跑去看 OpenAI 的網站,靠北呀啊就 Azure 贊助的。那當然在正式上線 ChatG...
囤積癖
607. Sales Person 題目敘述 SQL Schema > Create table If Not Exists SalesPerson (sales_id int, name varchar(255), salary int, commission_rate int, hire_date date) Create table If Not Exists Company (com_id int, name varchar(255), city varchar(255)...
最近因為公司的需求,可能要導入 GraphQL 到專案裡面,所以去做了些研究開始學習 GraphQL。
過程中實在是有許多地方的概念,很難搞懂(老了學不來了QQ)。在此紀錄自己學習後的理解,希望會幫助到入門的人,也希望有大大提點不正確的地方(拜託鞭我,我抖 M 不抖音)。
這邊先簡介一下 GraphQL,GraphQL 是由 Facebook 開源出來的專案。其特點是所見即所得的存取資料方式。通常在 RESTful 的 API 設計下,一個頁面可能需要多個 API 才能得到全部要呈現的資料,但是 GraphQL 只有一個 API,可以減少 HTTP 的來往次數。內建類型檢查的功能。個人使用至今,覺得缺點就是會增加 Backend 維護的複雜度。
以下我們可以從 GraphQL 官網的 Banner 看到使用的方式,先在 Backend 定義資料的格式,在前端建立想要的資料結構,最後回傳跟 query 一樣的資料結構 — 這就是所說的所見即所得。
GraphQL 範例
這邊必須抱怨一下,我看了許多介紹都沒提到 Resolver(尤其官網)。覺得缺少這塊會讓人學習上很大的缺陷。
接下來會分為三個部分來介紹:
Object Type
Query Schema
Resolver
這篇主要講述 Object Type 的部分,但是還是會包含一些 Query Schema 的東西。以下是範例:
type Character {
name: String!
appearsIn: [Episode]!
oldField: String @deprecated
length(unit: LengthUnit = METER, sort: String): Float
}
extend type Character {
age: String
}
Object Type 是 GraphQL 的基本元件,定義 Front End 可以從 Server 得到什麼類型的資料,以及其 Field。以上面為例:
type 是宣告了這是哪種類型的 Object Type
Character 宣告此 Object Type 的名稱,不能以 __為開頭
name 、 appearsIn 、 oldField、 length 則是其擁有的欄位,後面接的就是其欄位的資料型態
length 後面括號內則是參數
extend 是指在宣告完 Object Type 後,還可以再額外擴充欄位
Field 的參數可以有多個,當參數是非必須的時候,則需要給定一個預設值。
birthday(year: Int = 1991): String
GraphQL 內有三個特殊的 Object Type — Query 、 Mutation、 Subscription,其使用上與一般的 Object Type 沒有差別,唯一的特殊點在於它們定義了 Entry Point。以上面 GraphQL 的 Banner 為例。第二個區域的 query 部分 project(name = "GraphQL") 就會需要在 Server 定義:
type Query {
product(name: String): Product
}
裡面宣告查詢的接口名稱 product 和其參數 name: String,最後是回傳的 Object Type Product。
Query:獲取資料
Mutation:更新資料
Subscription:訂閱資料
我們可以從上面的範例,看到 Field 後面會接上該 Field 資料的類型。在 GraphQL 裡面稱之為 Scalar,其內建有:
ID:接受 String 和 Int 格式,但最後都會轉成 String
String:UTF-8 字符,由 " 包裹
Int
Float
Boolean
除了內建的 Scalar 之外,GraphQL 也允許自定義 Scalar。其轉換的方式需要在 Resolver 設定
// Object Type
scalar Time
type Birthday {
date: Time
}
type Query {
birthday: Birthday
}
// resolver
const { GraphQLScalarType } = require("graphql");
const { Kind } = require('graphql/language');
Time: new GraphQLScalarType({
name: 'Time',
description: 'Date custom scalar type',
parseValue: (value) => new Date(value), // 接收
serialize: (value) => new Date(value), // 回傳
parseLiteral: (ast) =>
if (ast.kind === Kind.INT) return parseInt(ast.value, 10);
return null;
});
一種特殊的 Scalar,限制 Field 的值在一個集合內。驗證值只能是集合中的某一個。
enum COLOR {
RED
GRAY
BLACK
WHITE
ORANGE
}
type Cat {
hair: COLOR
}
在 GraphQL 中預設 Field 的值可以為 null ,如果想要限定不能為 null 時,則在後面 scalar 加上 ! 。
List 則是讓資料回傳陣列的方式,方法是用 [] 將 Scalar 包裹起來,另外也可搭配 ! 使用。
Example 1:
myField: [String!]
myField: null // 有效
myField: [] // 有效
myField: ['a', 'b'] // 有效
myField: ['a', null, 'b'] // 錯誤
Example 2:
myField: [String]!
myField: null // 錯誤
myField: [] // 有效
myField: ['a', 'b'] // 有效
myField: ['a', null, 'b'] // 有效
最後將介紹 Interface 和 Union,這兩個概念對我來說是比較難懂的,到現在也不確定我的理解正不正確。希望有不同見解的人多多留言交流一下。
Interface & Union 在使用上常常會搭配 Variables 和 Inline Fragment 使用。因為 Interface & Union 可以相連多個 Object Type,但是最後只會回傳一個 Type,所以需要 Variables 來做篩選。Inline Fragment 則是用來獲得特定 Object Type 的資料。
Interface 是一個抽象類型,包含某些 Field。而 Object Type 必須包含這些 Field 才能實現這個 Interface。看完官網的這段話後,我真的在想說是在說三小。在看過其他的文章和在社群上討論後,個人理解 Interface 主要使用情境和功能在於,當不同的 Object Type 有許多相同或共用的 Field 時,可以使用 Interface 來做更好的管理。
interface Animal {
weight: Float
height: Float
name: String
}
interface Bird {
fly: Boolean
}
// Dog 裡面還是必須要重新宣告 Animal 裡面的 Fields,有點蠢但沒辦法,可以改進的空間
// 除了 Animal 裡面的 Fields,也能有自己額外的 Fields
type Dog implements Animal {
weight: Float
height: Float
name: String
master: String
}
// 一個 Object Type 可以實現複數個 interface
type Eagle implements Animal & Bird {
weight: Float
height: Float
name: String
fly: Boolean
color: String
}
// 最後是回傳 interface
type Query {
search(text: String!): Animal!
}
當在存取資料時,相同 interface 的欄位就可以直接 query 出來,但是額外的部分就需要使用 Inline Fragment 了
query {
search(text: "Dog") {
name
weight
height
// Inline Fragment 的部分,下一篇會介紹
... on Dog {
master
}
}
}
Union 和 Interface 類似,但並不指定 Object Type 之間的任何共同欄位。所以資料都必須使用 Inline Fragment 來取得。適用於從單個 Field 回傳不相交的 Object Type。
union SearchResult = Photo | Person
type Person {
name: String
age: Int
}
type Photo {
height: Int
width: Int
}
type SearchQuery {
firstSearchResult: SearchResult
}
// Query
# 無效查詢
{
firstSearchResult {
name
height
}
}
# 正確查詢
{
firstSearchResult {
... on Person {
name
}
... on Photo {
height
}
}
}
這個 Object Type 需要搭配第二篇的 Variables 一起看可能會比較懂。在 Front End 發送請求的時候,有時候會需要傳入較為複雜的變數,而不僅僅只是個 Scalar 的時候,就可以在 Server 用 Input 來定義,可以使用的變數。
input Search {
key: String!
}
# Front End Query
query SearchKey ($search: Search){
search(key: $search)
}
# Variables
{
key: "Popping"
}