欄位(Fields)
簡單而言,GraphQL 是關於請求對象上的特定欄位。我們以一個非常簡單的查詢以及其結果為例:
{
hero {
name
}
}
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
你立即就能發現,查詢和其結果擁有幾乎一樣的結構。這是 GraphQL 最重要的特性,因為這樣一來,你就總是能得到你想要的數據,而服務器也準確地知道客戶端請求的欄位。
name 欄位返回 String 類型,在這個示例中是《星球大戰》主角的名字是:"R2-D2"。
對了,還有一點 —— 上述查詢是可交互的。也就是你可以按你喜歡來改變查詢,然後看看新的結果。嘗試給查詢中的 hero 對象添加一個appearsIn 欄位,看看新的結果吧。
在前一例子中,我們請求了我們主角的名字,返回了一個字元串類型(String),但是欄位也能指代對象類型(Object)。這個時候,你可以對這個對象的欄位進行次級選擇(sub-selection)。GraphQL 查詢能夠遍歷相關對象及其欄位,使得客戶端可以一次請求查詢大量相關數據,而不像傳統 REST 架構中那樣需要多次往返查詢。
{
hero {
name
# 查詢可以有備註!
friends {
name
}
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
注意這個例子中,friends 返回了一個數組的項目,GraphQL 查詢會同等看待單個項目或者一個列表的項目,然而我們可以通過 schema 所指示的內容來預測將會得到哪一種。
參數(Arguments)
即使我們能做的僅僅是遍歷對象及其欄位,GraphQL 就已經是一個非常有用的數據查詢語言了。但是當你加入給欄位傳遞參數的能力時,事情會變得更加有趣。
{
human(id: "1000") {
name
height
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
在類似 REST 的系統中,你只能傳遞一組簡單參數 —— 請求中的 query 參數和 URL 段。但是在 GraphQL 中,每一個欄位和嵌套對象都能有自己的一組參數,從而使得 GraphQL 可以完美替代多次 API 獲取請求。甚至你也可以給 標量(scalar)欄位傳遞參數,用於實現服務端的一次轉換,而不用每個客戶端分別轉換。
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 5.6430448
}
}
}
參數可以是多種不同的類型。上面例子中,我們使用了一個枚舉類型,其代表了一個有限選項集合(本例中為長度單位,即是 METER 或者 FOOT)。GraphQL 自帶一套默認類型,但是 GraphQL 服務器可以聲明一套自己的定製類型,只要能序列化成你的傳輸格式即可。
操作名稱(Operation name)
這之前,我們都使用了簡寫句法,省略了 query 關鍵字和查詢名稱,但是生產中使用這些可以使我們代碼減少歧義。
下麵的示例包含了作為操作類型的關鍵字 query 以及操作名稱 HeroNameAndFriends:
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
操作類型可以是 query、mutation 或 subscription,描述你打算做什麼類型的操作。操作類型是必需的,除非你使用查詢簡寫語法,在這種情況下,你無法為操作提供名稱或變量定義。
操作名稱是你的操作的有意義和明確的名稱。它僅在有多個操作的文檔中是必需的,但我們鼓勵使用它,因為它對於調試和服務器端日誌記錄非常有用。 當在你的網路或是 GraphQL 服務器的日誌中出現問題時,通過名稱來從你的代碼庫中找到一個查詢比嘗試去破譯內容更加容易。 就把它想成你喜歡的程式語言中的函數名。例如,在 JavaScript 中,我們只用匿名函數就可以工作,但是當我們給了函數名之後,就更加容易追蹤、調試我們的代碼,並在其被調用的時候做日誌。同理,GraphQL 的查詢和變更名稱,以及片段名稱,都可以成為服務端側用來識別不同 GraphQL 請求的有效調試工具。
變量(Variables)
目前為止,我們將參數寫在了查詢字元串內。但是在很多應用中,欄位的參數可能是動態的:例如,可能是一個"下拉菜單"讓你選擇感興趣的《星球大戰》續集,或者是一個搜索區,或者是一組過濾器。
將這些動態參數直接傳進查詢字元串並不是好主意,因為這樣我們的客戶端就得動態地在運行時操作這些查詢字元串了,再把它序列化成 GraphQL 專用的格式。其實,GraphQL 擁有一級方法將動態值提取到查詢之外,然後作為分離的字典傳進去。這些動態值即稱為變量。
使用變量之前,我們得做三件事:
使用 $variableName 替代查詢中的靜態值。
聲明 $variableName 為查詢接受的變量之一。
將 variableName: value 通過傳輸專用(通常是 JSON)的分離的變量字典中。
全部做完之後就像這個樣子:
# { "graphiql": true, "variables": { "episode": JEDI } }
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
這樣一來,我們的客戶端代碼就只需要傳入不同的變量,而不用構建一個全新的查詢了。這事實上也是一個良好實踐,意味著查詢的參數將是動態的。
變更(Mutations)
GraphQL 的大部分討論集中在數據獲取,但是任何完整的數據平臺也都需要一個改變服務端數據的方法。
REST 中,任何請求都可能最後導致一些服務端副作用,但是約定上建議不要使用 GET 請求來修改數據。GraphQL 也是類似 —— 技術上而言,任何查詢都可以被實現為導致數據寫入。然而,建一個約定來規範任何導致寫入的操作都應該顯式通過變更(mutation)來發送。
就如同查詢一樣,如果任何變更欄位返回一個對象類型,你也能請求其嵌套欄位。獲取一個對象變更後的新狀態也是十分有用的。我們來看看一個變更例子:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
注意 createReview 欄位如何返回了新建的 review 的 stars 和 commentary 欄位。這在變更已有數據時特別有用,例如,當一個欄位自增的時候,我們可以在一個請求中變更並查詢這個欄位的新值。
你也可能注意到,這個例子中,我們傳遞的 review 變量並非標量。它是一個輸入對象類型,一種特殊的對象類型,可以作為參數傳遞。你可以在 Schema 頁面上瞭解到更多關於輸入類型的信息。