英文原版地址:https://www.howtographql.com/...前端
GraphQL一般被認爲是前端的API技術,由於它使客戶端可以以更好的方式獲取數據。 可是既然是API,固然是在服務器端實現的。 由於GraphQL使服務器開發人員可以專一於描述數據,而不是實現和優化特定的接口,因此在服務器上也有不少好處。git
GraphQL經過定義Schema和查詢語言,來從Schema中檢索數據,但卻不只僅是這一種方式。更是一種實際的執行算法,用於將這些查詢轉換爲結果。 該算法的核心很是簡單:查詢逐字段遍歷,爲每一個字段執行「解析器」。 讓假設咱們有如下模式:github
type Query { author(id: ID!): [Author] } type Author { posts: [Post] } type Post { title: String content: String }
下面是咱們使用該Schema發送到服務器的查詢:算法
query { author(id: "abc") { posts { title content } } }
首先要關注的是查詢中的每一個字段均可以與一個類型相對應:數據庫
query: Query { author(id: "abc"): Author { posts: [Post] { title: String content: String } } }
如今,咱們能夠輕鬆地找到並運行服務器中每一個字段對應的解析器。從查詢類型開始執行,並之外層爲先。這意味着咱們先運行Query.author
的解析器。而後,咱們將該解析器的結果傳遞給它的子解析器,Author.posts
的解析器。在下一級,結果是一個列表,在這種狀況下,算法會依次在每一個元素上執行一次。因此最終執行工做以下:後端
Query.author(root, { id: 'abc' }, context) -> author Author.posts(author, null, context) -> posts for each post in posts Post.title(post, null, context) -> title Post.content(post, null, context) -> content
最終,執行算法將全部結果數據正確的放在定義好的結構中,並返回。
須要注意的是,大多數GraphQL服務器實現將提供「默認解析器」 - 所以您沒必要爲每一個單個字段指定解析器函數。例如,在GraphQL.js中,當解析器的父對象包含具備正確名稱的字段時,不須要指定解析器。
在Apollo博客上的「GraphQL Explained「文章中,可更深刻的瞭解GraphQL執行狀況。服務器
你可能會注意到上述執行策略的一件事是,它有點幼稚。例如,若是你有從後端API或數據庫提取的解析器,則在執行一個查詢期間可能會屢次調用該後端。讓咱們假設,咱們想獲取幾個帖子的做者,就像這樣:函數
query { posts { title author { name avatar } } }
若是這些是博客上的帖子,極可能不少帖子將有相同的做者。因此若是咱們須要一個API調用來獲取每一個做者對象,咱們可能會意外地爲同一個對象發出多個請求。例如:工具
fetch('/authors/1') fetch('/authors/2') fetch('/authors/1') fetch('/authors/2') fetch('/authors/1') fetch('/authors/2')
咱們如何解決這個問題?讓咱們聰明一點。咱們能夠將fetch函數封裝在一個工具函數中,該實函數將等待全部的解析器運行後,再確保只fetch每一個元素一次:post
authorLoader = new AuthorLoader() // Queue up a bunch of fetches authorLoader.load(1); authorLoader.load(2); authorLoader.load(1); authorLoader.load(2); // Then, the loader only does the minimal amount of work fetch('/authors/1'); fetch('/authors/2');
咱們能作得更好嗎?固然,若是咱們的API支持批量請求,咱們只能對後端執行一次提取操做,以下所示:
fetch('/authors?ids=1,2')
這也能夠封裝在上面的工具函數中。
在JavaScript中,可使用 DataLoader 的工具實現上述策略,其餘語言也有相似的工具。