Fragment
你可以將操作的定義及 fragment 放入 GraphQL 查詢文件。fragment 是可在多個操作中重複使用的選擇組(SelectionSet)。我們回到 Snowtooth 的 GraphQL Playground 介面,看一下這個 query:
1 | query { |
這個查詢請求的是 Jazz Cat 纜車與 River Run 雪道的資訊。Lift 的選擇組裡有 name、status、capacity、night 與 elevationGain。我們想要取得的 River Run 雪道資訊有一些欄位與 Lift 型態的欄位相同。我們可以建立一個 fragment 來協助減少 query 重複的地方:
1 | fragment liftInfo on Lift { |
我們用 fragment 關鍵字來建立 fragment。fragment 是屬於特定型態的選擇組,所以你必須在 fragment 的定義中指定它所屬的型態。這個範例的 fragment 稱為 liftInfo,它是 Lift 型態的選擇組。
當我們要在另一個選擇組中加入 liftInfo fragment 欄位時,可在 fragment 名詞前面加上三個句點:
1 | query { |
這種語法類似 JavaScript 的 spread 運算子。這三個句點可讓 GraphQL 將 fragment 的欄位指派給當前的選擇組。在這個範例中,我們在 query 的兩個地方使用同一個 fragment 來選擇 name、status、capacity、night 與 elevationGain。
我們無法將 liftInfo fragment 加入 Trail 選擇組,因為 liftInfo 只定義了 Lift 型態的欄位。我們可以加入另一個雪道 Trail 型態欄位的 fragment trailInfo:
1 | query { |
在這個範例中,我們建立了一個稱為 trailInfo 的 fragment,並在 query 的兩個地方使用它。我們也在 trailInfo fragment 中使用 liftInfo fragment 來選擇與它連接的纜椅資料。你可以視需求建立任意數量的 fragment,並交換使用它們。在 River Run Trail query 使用的選擇組中,我們將 fragment 與想要選擇的、關於 River Run 雪道的其他資料結合。你可以一併使用 fragment 與選擇組的其他欄位,也可以在單個選擇組中結合多個屬於同樣型態的 fragment:
1 | query { |
fragment 有個很棒的優點在於,你只需要修改一個 fragment,就可以修改在許多不同的 query 裡面的選擇組:
1 | fragment liftInfo on Lift { |
這樣修改 liftInfo fragment 的選擇組會讓使用這個 fragment 的每一個 query 選擇較少的資料。
變動 Mutation
到目前為止,我們已討論許多關於讀取資料的事情了。我們用 query 來描述在 GraphQL 中發生的所有讀取。若要寫入新資料,就要使用 mutation (變動)。mutation 的定義類似 query ,它們都有名稱,也可以擁有 “可回傳物件型態或純量型態的選擇組” ,不同之處在於 mutation 可執行一些影響後端資料狀態的修改。
例如,這是個很危險的 mutation:
1 | mutation burnItDown { |
Mutation 是一種根物件型態。API 的 schema 定義了這個型態可用的欄位 (field)。上述的 Mutation API 擁有強大的威力,可清除所有的資料,它實作了一個 deleteAllData 的欄位,當所有資料都被成功的刪除,這個欄位會回傳一個純量型態: true,或者如果出錯了,會回傳 false。資料究竟會不會被刪除是藉由實作這個 API 來決定。
我們來看另外一個範例,這次我們要創造一些東西,而不是摧毀東西:
1 | mutation createSong { |
我們可以使用這個範例來創建新歌曲,利用引數將 title、numberOne 狀態與 performerName 傳給這個 mutation 之後,它會將這首新歌加入資料庫。如果這個 mutation 欄位 addSong 會回傳物件,你就要在這個 mutation 後面加入一個選擇組。在本例中,mutation 完成後會回傳 Song 型態的物件,裡面有剛才創建的歌曲的資料。我們可以在 mutation 後面選擇新歌的 id、title 與 numberOne 狀態的選擇組:
1 | { |
上面是這個 mutation 可能的回應。如果出錯,這個 mutation 會在 JSON 回應裡面回傳錯誤,而不是新建的 Song 物件。
我們也可以使用 mutation 來更改既有的資料。當我們想要更改 Snowtooth 的纜椅狀態時,也可以使用 mutation:
1 | mutation closeLift { |
這個 mutation 將 Jazz Cat 纜椅的狀態從 open 改成 closed。我們可以在 mutation 後面的選擇組裡面選擇被更改的 Lift 的欄位。在此,我們取得被改變的纜椅的 name,以及更新後的 status。
使用查詢變數
現在我們已經會使用 mutation 引數傳送新字串值來更改資料了,另一種做法是使用輸入變數,以變數取代 query 內的靜態值可讓我們可以傳入動態值。在 addSong mutation,我們要用變數名稱來取代字串,在 GraphQL 中,變數必定以 $ 字元開頭:
1 | mutation createSong($title:String! $numberOne:Boolean $by:String!) { |
我們將靜態值換成 $variable,接著宣告的 mutation 可接收 $variable。GraphiQL 與 Playground 都有一個 Query Variables 視窗 (左下角,可把空間往上拉大),我們在這裡用 JSON 物件來傳送輸入資料,務必用正確的變數名稱作為 JSON 鍵:
1 | { |
當你傳送引數資料時,變數的功能很強大,它不但可讓你的 mutation 在測試的過程中更有條理,當你連接用戶端介面時,使用動態輸入也有很大的幫助。
訂閱 Subscription
GraphQL 的第三種操作類型是訂閱 (subscription)。有時用戶端想要取得伺服器傳送的即時更新。訂閱可讓我們監聽 GraphQL API 的即時資料變更。
GraphQL 的訂閱功能來自 Facebook 的實際使用案例。這個團隊想要在不重新整理網頁的情況下,顯示關於貼文獲得的贊 (Live Likes)數量的即時資訊。Live Likes 是以訂閱來製作的及時使用案例。每一個用戶端都會訂閱 like 事件,並即時看到 like 的更新。
如同 mutation 與 query,subscription 是一種根型態。你必須在 API schema 的 subscription 型態下的欄位中定義用戶端可以監聽的資料變更。編寫 GraphQL query 來監聽 subscription 的做法類似定義其它操作的方式。
例如,在 Snowtooth 中,我們可以用 subscription 監聽任何纜椅狀態的變動:
1 | subscription { |
當我們執行這個 subscription 時,會用 WebSocket 來監聽纜椅狀態的改變。請留意,在 GraphQL Playground 按下播放按鈕後不會立刻收到回傳的資料。當 subscription 被送到伺服器時,這個 subscription 會監聽資料的任何改變。
如果你想要看到 subscription 收到資料,就必須做出改變。你必須打開一個新視窗或標籤,用 mutation 來傳送改變。 如果使用的是 GraphQL Playground,視窗上端可以直接打開一個新標簽來加入 mutation。 我們在新視窗或標籤送一個改變纜椅狀態的 mutation:
1 | mutation closeLift { |
回到 subscription 的視窗,將會收到被更新的資料,以及資料被送到 subscription 的時間。
與 query 和 mutation 不同的是,subscription 會保持開啟。接下來每當有纜椅的狀態改變時,新的資料就會被推送到這個 subscription。若要停止監聽狀態的變動,你必須取消 subscription。當你使用 GraphQL Playground 時,只要按下停止按鈕即可。不幸的是,用 GraphiQL 來取消 subscription 唯一的做法是關閉運行該 subscription 的瀏覽器標籤。
自我查詢 Introspection
自我查詢 (introspection)是 GraphQL 最強大的功能之一。自我查詢是指查詢目前的 API 的 schema。自我查詢是將這些精心建構的 GraphQL 文件加入 GraphiQL 與 Playground 介面的方式。
你可以傳送 query 給每一個 “可回傳特定 API 的 schema 資料” 的 GraphQL API。例如,如果你要知道可在 Snowtooth 中使用哪種 GraphQL 型態,可以執行 __schema query 來查看該資訊:
1 | query { |
當你執行這個 query 時,可以看到這個 API 可用的每一個型態,包括根型態、自訂型態,甚至純量型態。如果你想要查看特定型態的資料,可執行 __type query,並用引數來傳送想要查詢的型態名稱:
1 | query liftDetails { |
這種自我查詢功能可讓你看到 Lift 型態可供查詢的所有欄位。當你想要瞭解新的 GraphQL API 時,找出根型態提供哪些欄位是很好的做法:
1 | query roots { |
自我查詢會遵循 GraphQL 查詢語言的規則。我們用 fragment 來簡化上述的 query,並查詢每個根型態的名稱與它們提供的欄位。自我查詢可讓用戶端知道目前的 API schema 如何運作。
抽象語法樹 (Abstract syntax tree, AST)
query 文件是個字串。當我們傳送 query 給 GraphQL API 時,字串會被解析成抽象語法樹,並在操作執行之前進行驗證。抽象語法樹 AST 是一種代表 query 的階層式物件,是個含有內嵌欄位的物件,裡面的欄位代表 GraphQL query 的細節。
解析程序的第一個步驟是將字串解析成一堆較小的片段,這個步驟包括將關鍵字、引數,甚至括號與冒號解析成單獨的標記,這個程序稱為詞法分析 (lexing 或 lexical analysis)。接下來將詞法分析後的 query 解析成 AST。使用 AST 可讓動態修改與驗證 query 的工作輕鬆許多。
例如,你的 query 一開始是 GraphQL 文件。文件至少有一個定義,也可能有一串定義。定義只有可能是兩種型態之一: OperationDefinition 與 FragmentDefinition。下面的文件範例有三個定義: 兩項操作 (Operation) 與一個 fragment:
1 | query jazzCatStatus { |
一個 OperationDefinition 只能含有三種操作型態之一: mutation、query 或 subscription。每一個操作定義都有 OperationType 與 SelectionSet。
在每一個操作後面的大括號內都有該操作的 SelectionSet,它們就是我們用引數來查詢的欄位。例如,Lift 欄位是 jazzCatStatus query 的 SelectionSet,而 setLiftStatus 欄位是 closeLift mutation 的選擇組。
選擇組可嵌套在另一個選擇組裡。jazzCatStatus query 有三個嵌套的選擇組。第一個 SelectionSet 含有 Lift 欄位。在它裡面有個 SelectionSet 含有 name、night、elevationGain 與 trailAccess 欄位。在 trailAccess 欄位內有另一個 SelectionSet,含有每個雪道的 name 與 difficulty 欄位。
GraphQL 可遍歷這個 AST 並且用 GraphQL 語言與目前的 schema 來驗證它的細節。如果查詢語言的語法是正確的,且 schema 含有我們請求的欄位與型態,該操作就會執行。如果情況不是如此,就會回傳特定的錯誤。
AST 是 GraphQL 很重要的成分。每一項操作都會被解析成 AST,以便對它進行驗證並最終執行它。