【譯】GraphQL官方中文文件-客戶端如何使用
- 客戶端:GraphQL是一種查詢語言(for API),即傳送Graphql請求
- 伺服器端:在執行時(runtime)使用你預先為你的資料定義好的型別系統(Type System)來處理客戶端傳送過來的Graphql請求。
它並沒有指定特定的資料庫或者儲存器,完全靠你現有的程式碼和資料支撐
建立一個GraphQL service 需要定義types和內部的fields,然後還需要一個解析函式來處理這些types.
舉例:一個GraphQL service會如下所示 定義types 和 fields
//who the logged in user is (me) type Query { me: User } // 針對User的查詢 type User { id: ID name: String } 複製程式碼
伺服器端需要對每一個Field解析
function Query_me(request) { return request.auth.user; } function User_name(user) { return user.getName(); } 複製程式碼
一旦一個GraphQL Service 啟動了,就可以接受GraphQL queries並檢驗和執行。先保證只處理預定好的types-fileds有關的query,然後執行解析函式並返回結果,示例如下
//傳送如下query { me { name } } //得到如下json { "me": { "name": "Luke Skywalker" } } 複製程式碼
Queries and Mutations(查詢和修改)
Fields(欄位)
最簡情況下,Graphql會向物件上請求特定的欄位,讓我們從一個簡單的例子開始
-
Query
{ hero { name } } 複製程式碼
-
Result
{ "data": { "hero": { "name": "R2-D2" } } } 複製程式碼
你可以立即發現上面的query和result的結構是一致的。這是GraqhQL的特性——以為你總是獲取到你想要的,並且服務端精確地知道客戶端想要哪些欄位。
name欄位返回了一個String 型別,在上例中也就是星球大戰中的hero("R2-D2")
在上面的例子中,我們僅僅是查詢了hero的名字並得到了一個String,但是fields不僅僅可以是類似name這樣一個變數的形式,也可以是一個Objects,這樣你就可以對“hero”中的欄位進行次級選擇(sub-selection ),GraphQL queries可以遍歷相關的Objects-Fields,從而使客戶端可以在一次請求中獲取大量的相關資料。往往我們在REST架構下可能會做多次的往返請求才能實現上述效果。下面是一個例子
-
Query
{ hero { name # Queries can have comments! # friends 就是上述所說的次級選擇,實現了相關查詢 :ox::beer: friends { name } } } 複製程式碼
-
返回結果
{ "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, ] } } } 複製程式碼
注意,friends欄位返回了一個數組。GraphQL queries會等同的對待單個的items或者列表,然而我們可以通過schema中推匯出我們期望返回是哪種型別。
Arguments(引數)
如果僅僅是做可以遍歷相關物件及其欄位的話,GraqhQL已經在獲取資料上非常有用了。但是當你在查詢時可以接受引數時,事情就會變得更加有趣了!
在REST風格的系統中,你只能傳遞一個引數集合--也就是http請求中的查詢引數與URL段。但是在GraphQL中,每一個欄位或內嵌物件都可以獲取一個引數集,這讓GraphQL徹底的取代了執行多次API獲取這種方式。你甚至可以向scalar fields傳參,來實現在服務端一次性的資料轉換而不是分別在每個客戶端做。
-
Query
{ human(id: "1000") { name # 這裡對高度做了傳參,要求單位是英尺 height(unit: FOOT) } } 複製程式碼
-
返回結果
{ "data": { "human": { "name": "Luke Skywalker", # 返回了英尺高度 :ox::beer: "height": 5.6430448 } } } 複製程式碼
引數可以有很多型別。在上例中,我們使用了一個列舉型別(Enumeration type),即一個有限的選擇的集合(set),也就是米 或者 英尺 等。 GraphQL預設內建了一個型別集, as long as they can be serialized into your transport format.
Read more about the GraphQL type system here.
Aliases(別名)
如果你敏銳的話,你可能已經發現了,既然返回的結果中的物件欄位與query中的相關欄位是匹配的但是並不包含引數,那麼你就不可以針對同一個欄位通過傳輸不同的引數來獲取不同的結果了。這就是我們需要別名aliases 的原因。
譯者注:當你需要針對某個欄位通過不同的引數在一個Query中完成所有的資料獲取時,你就需要用到別名了
- query
# 星球大戰北京。。。。 帝國 絕地武士,關注這裡的別名就行了 { empireHero: hero(episode: EMPIRE) { name } jediHero: hero(episode: JEDI) { name } } 複製程式碼
- result
{ "data": { "empireHero": { "name": "Luke Skywalker" }, "jediHero": { "name": "R2-D2" } } } 複製程式碼
譯者注 :如果沒有別名,你不可能在一個request對hero這個欄位獲取到兩種資料的,所以通過別名傳遞不同的引數來獲得兩份資料,並且在返回的結果中也不是hero了,而是替換成了 empireHero 和 jediHero,這樣也方便了客戶端的處理。
Fragments(片段):可複用單元
加入我們App有一個複雜的頁面,在其中兩種hero的陣營分別佔據頁面的一邊,你可以發現query一下子就複雜起來了,因為我們會重複的去宣告某些欄位。
這也就是為什麼GraphQL包含了fragments 這種可複用,fragments 讓你可以構建一個欄位集並且在query中不停的複用。下面是例子
- query
{ leftComparison: hero(episode: EMPIRE) { ...comparisonFields } rightComparison: hero(episode: JEDI) { ...comparisonFields } } fragment comparisonFields on Character { name appearsIn friends { name } } 複製程式碼
- result
{ "data": { "leftComparison": { "name": "Luke Skywalker", "appearsIn": ["NEWHOPE", "EMPIRE","JEDI"], "friends": [ {"name": "Han Solo"}, {"name": "Leia Organa" }, ] }, "rightComparison": { "name": "R2-D2", "appearsIn": ["NEWHOPE","EMPIRE","JEDI"], "friends": [{"name": "Luke Skywalker" } ] } } } 複製程式碼
你可以發現如果沒有fragments 的話,那些欄位會重複的出現。片段的概念在將複雜的應用資料切分的時候經常被使用到,特別是當你有多個UI元件(有不同的片段)在初始化資料獲取時