介面是行為的定義,確定了跟系統互動的介面。 行為的實體是函式,函式包括定義和實現,而介面只關注函式的定義。 函式定義包括傳人蔘數和返回值,包含了引數名稱和型別,包含了返回值的型別。
GraphQL是介面
GraphQL本質上是一種介面的定義規範,裡面包括了種型別定義和函式定義(名稱,引數,返回值),可以精確的定義了前後端互動的介面。
GraphQL不是一般的介面
GraphQL是隻定義介面,不關心實現,本身是語言不相關的。
這意味著GraphQL可以作為連線不同系統的統一規範。
GraphQL包含嚴格的型別定義,這種定義可以用來進行程式碼生成,結合本身有提供很多輔助開發工具,可以輔助解決介面的定義、實現、文件、除錯問題。
以上所說的這些,很多rpc平臺,像grpc、thrift都能解決。著這個角度講,GraphQL就是一種rpc協議。
如果要說不同之處,GraphQL設計之處的目的是解決前後端互動的問題。前後端互動網路傳輸是一個問題。
傳統的rpc返回結果的時候回返回完整的資料,但是很多時候我們只需要結果中的部分資料,這樣傳輸無用資料會造成頻寬浪費,同時也會影響處理速度,對於響應要求高或者網路頻寬不夠的應用不利,移動應用就屬於這類。
使用GraphQL介面時,可以基於定義約定想要的東西,然後GraphQL會根據請求,只返回想要的資料,這點類似於SQL。同時,可以在一次GraphQL請求傳送多個函式呼叫請求,這樣可以減少請求的傳送量,從而提高響應速度。
舉例說明
下面我們用Go語言定義了一個簡單的型別和介面。
// 定義的User型別
type User struct {
ID int
Name string
Age int
Posts []Post
}
type Post struct {
ID int
Title string
Owner User
}
// 定義的查詢介面
type Query interface {
FetchUsers() []User
FetchPosts(userID int) []Post
GetUserById(id int) *User // 定義了函式的引數和返回值
}
// 定義的修改介面
type Mutation interface {
CreateUser(name string, age int) User
DeleteUser(id int) *User
}
複製程式碼
我們知道,介面如果成功呼叫,會返回一個完整的資料。比如,GetUserById成功呼叫後會返回一個完整的User給我們。 這裡包括大量User的全部屬性, 形如User {ID: 0, Name: "Alex", Age: 28}。 但是如果我們頻寬太窄,只需要User的Name資訊呢? 這裡就是graphql跟介面不同的地方。像SQL一樣,使用GraphQL你可以宣告你需要什麼屬性。
{
GetUserById(id: 1) {
Name
}
}
複製程式碼
介面,你呼叫一個函式,返回一個結果。你返回多種結果,呼叫多次函式。 使用GraphQL,你能夠把多個對資料的要求放在一個請求裡面。
{
GetUserById(id: 1) {
Name
}
FetchPosts(userId: 1) {
title
}
}
複製程式碼
而且,GraphQL還支援順著型別定義巢狀的抓取資訊。 上面的查詢也可以寫成下面的形式。
{
GetUserById(id: 1) {
Posts {
title
}
}
}
複製程式碼
當然你也可以喪心病狂的不斷巢狀抓取資料。
{
GetUserById(id: 1) {
Posts {
title
Owner {
Name {
Posts {
title
...
}
}
}
}
}
}
複製程式碼
這種按照介面的型別定義,不斷巢狀的抓取資料正是GraphQL中Graph的由來,表達了Graph + QL = 按圖 + 索驥內涵。 當然這種變態的抓取是非常恐怖的,如果有壞人故意發這種請求搞死我們,我們就完了,所以必須有一種方式來限制這種行為。 好在,我們可以對使用者的請求進行AST語法分析,限制巢狀的深度,比如,我們可以讓你最多巢狀3層。
總結
GraphQL本質上是一種為了解決前後端互動問題二設計的介面協議。很好的解決了介面的定義、文件、除錯、使用等問題,目測會越來越火。