GraphQL 入門: Apollo Client - 持久化GraphQL查詢概要

GraphQL 入門: 簡介
GraphQL 入門: Apollo Client - 簡介
GraphQL 入門: Apollo Client - 安裝和配置選項
GraphQL 入門: Apollo Client - 鏈接到數據
GraphQL 入門: Apollo Client - 網絡層
GraphQL 入門: Apollo Client - 開發調試工具
GraphQL 入門: Apollo Client - 持久化GraphQL查詢概要
GraphQL 入門: Apollo Client - 存儲API
GraphQL 入門: Apollo Client - 查詢(Batching)合併git

摘要: 本文采用 Elixir 語言開發的 Absinthe 做爲 GraphQL 的服務器端實現, 使用 Javascript 語言開發的 Apollo Client 做爲 GraphQL 的客戶端實現.github

1. 持久化查詢的概念

持久化查詢, 是一種避免客戶端直接在查詢請求中包含查詢文檔的一種方式, 客戶端只須要傳遞給要執行查詢的ID, 服務器經過ID查詢到GraphQL文檔, 並在服務器端執行的過程. npm

優缺點:json

客戶端發送一個巨大的查詢過量消耗服務器的資源
clipboard.pngsegmentfault

客戶端能夠執行任意查詢,容易致使安全問題
clipboard.png安全

使用持久化查詢的目的:服務器

  • 避免了客戶端發送GraphQL文檔, 減小網絡流量網絡

  • 因爲GraphQL巨大的靈活性, 這也給安全帶來了挑戰, 這種機制實際上就是查詢白名單. 只有在白名單中的查詢纔是能被服務器執行的.ide

從直接發送查詢文檔轉換到持久化查詢的效果:函數

發送一個巨大的查詢再也不是一個問題
clipboard.png

客戶端只能查詢服務器支持的限制性的GraphQL查詢(經過ID)
clipboard.png

2. 要求

客戶端須要使用到 persistgraphql 工具來生成對應的查詢描述文檔. 用於完成查詢到ID的映射關係.

服務器端也須要作一樣的事情.

3. 實施方案

本節描述了服務器端和客戶端的具體實現.

3.1. 服務器端

  • 服務器端採用Elixir語言做爲開發語言和運行環境

  • 採用 Absinthe 包做爲 GraphQL 查詢的處理模塊

  • 服務器端使用 Absinthe.Plug.DocumentProvider.Compiled 模塊讀取由 persistgraphql 工具生成的extracted_queries.json文件來達到和客戶端一致.

3.1.1. 服務器端查詢轉換原理

服務器接收到客戶端發過來的查詢以下:

{
  id: < 查詢 ID >,
  variables: < 變量JSON對象 >,
}

經過查找ID, 把查詢轉換爲以下形式, 把ID替換爲查詢文本:

{
  query: < GraphQL文檔 >,
  variables: < 變量JSON對象 >
}

3.1.2. 實現細節

建立一個文檔Provider:

defmodule MyApp.ExtractedQueryProvider do
  use Absinthe.Plug.DocumentProvider.Compiled

  provide File.read!("/path/to/extracted_queries.json")
  |> Poison.decode!
  |> Map.new(fn {k, v} -> {v, k} end) # invert key/value
end

添加該文檔 Provider 到 Absinthe.Plug 配置:

plug Absinthe.Plug,
  schema: MyApp.Schema,
  document_providers: [
    Absinthe.Plug.DocumentProvider.Default,
    MyApp.ExtractedQueryProvider
  ]

當構建服務器端項目的時候, 讀取 extracted_queries.json 文件的內容, 解析, 並編譯爲Elixir模塊, 轉換爲中間表示, 並執行驗證. 這樣的持久化查詢請求就像經過ID請求一個普通的對象同樣.

注意:
詳細的服務器實現方法,請參考這個 Issue
另外Absinthe對持久化查詢的支持須要用到最新的代碼, 請使用 absinthe_plugv1.3.0-alpha.0 及以上版本.

3.2. 客戶端

本節描述了持久化查詢的客戶端實現.

3.2.1 基本原理

客戶端經過讀取 extracted_queries.json 文件, 把讀取到的文件內容轉換爲一個JSON對象, 客戶端向服務器發送請求以前, 先經過把整個GraphQL查詢做爲對象的字符串Key, 去找到對應的ID, 經過查找到的ID去執行GraphQL查詢.

這個GraphQL查詢文本查詢ID映射的JSON對象格式以下:

{
 < print(transform(GraphQL Document)) >: < ID >,
}

Javascript 只容許字符串和數字做爲鍵, 所以咱們經過 graphql-js 模塊的 print 函數來解決這個問題.

最終, 客戶端經過下面的僞代碼執行 GraphQL 查詢:

query(request: Request) {
 // 在 request.query 結構中查找對應的ID
 // 找到對應的ID
 // 經過GraphQL查詢變量把ID傳遞給服務器
 // 返回一個Promise對象用於獲取服務器的返回結果
}

3.2.2 實現細節

安裝 persistgraphql 命令行工具:

npm install --save persistgraphql 
# 或
yarn add persistgraphql
# 全局安裝
yarn global add persistgraphql

persistgraphql 有兩個命令行參數, 分別表示輸入和輸出, 輸入能夠是當個文件或者目錄名稱, 路徑可使相對或絕對路徑, 安裝好後, 能夠執行:

persistgraphql queries.graphql

生成 extracted_queries.json 文件. 若是指定的輸入是一個目錄, 那麼 persistgraphql 會去查找該目錄下的所後綴爲 .graphql 的文件. 你能夠用GitHunt-React 這個項目來實際練習一下.

git clone git@github.com:apollographql/GitHunt-React.git
cd GitHunt-React
persistgraphql ui/graphql extracted_queries.json

客戶的實現方法可參考資料: 使用Apollo Client持久化GraphQL查詢

4. 參考資料

相關文章
相關標籤/搜索