如何在 Postgres 上使用 GraphQL 訂閱構建實時應用程序

已發表: 2022-03-10
快速總結 ↬構建實時應用程序很困難。 然而,GraphQL 正在迅速顛覆這種現狀。 讓我們探索一下 GraphQL 是什麼,然後通過構建一個投票應用程序來試一試,用戶可以在該應用程序中投票並實時更新屏幕上的聚合結果。

在本文中,我們將了解構建實時應用程序所涉及的挑戰,以及新興工具如何通過易於推理的優雅解決方案來解決這些挑戰。 為此,我們將使用 Postgres、GraphQL、React 構建一個實時投票應用程序(例如具有實時總體統計數據的 Twitter 投票)!

主要關注點是設置後端(部署即用型工具、模式建模),以及前端與 GraphQL 集成的方面,而不是前端的 UI/UX(一些 ReactJS 知識會有所幫助)。 教程部分將採用按數字繪製的方法,因此我們將克隆一個用於模式建模的 GitHub 存儲庫,以及 UI 並對其進行調整,而不是從頭開始構建整個應用程序。

一切 GraphQL

你知道關於 GraphQL 你需要知道的一切嗎? 如果您有疑問,Eric Baer 會為您提供有關其起源、缺點以及如何使用它的基礎知識的詳細指南。 閱讀相關文章 →

在您繼續閱讀本文之前,我想提一下以下技術(或替代品)的工作知識是有益的:

  • 反應JS
    通過遵循客戶端庫文檔,可以將其替換為任何前端框架、Android 或 IOS。
  • Postgres
    您可以使用其他數據庫,但使用不同的工具,本文中概述的原則仍然適用。

您還可以非常輕鬆地將本教程上下文改編為其他實時應用程序。

本教程中構建的投票應用程序中的功能演示
我們將要構建的投票應用程序中的功能演示。 (大預覽)

如底部隨附的 GraphQL 有效負載所示,我們需要實現三個主要功能:

  1. 獲取投票問題和選項列表(左上角)。
  2. 允許用戶為給定的投票問題投票(“投票”按鈕)。
  3. 實時獲取投票結果並將其顯示在條形圖中(右上角;我們可以忽略該功能以獲取當前在線用戶列表,因為它是此用例的精確副本)。
跳躍後更多! 繼續往下看↓

構建實時應用程序的挑戰

構建實時應用程序(尤其是作為前端開發人員或最近過渡到成為全棧開發人員的人)是一個難以解決的工程問題。 這通常是當代實時應用程序的工作方式(在我們的示例應用程序的上下文中):

  1. 前端使用一些信息更新數據庫; 用戶的投票被發送到後端,即投票/選項和用戶信息( user_idoption_id )。
  2. 第一次更新會觸發另一個服務,該服務會聚合投票數據以呈現輸出,該輸出會實時轉發回應用程序(每次有人投票時;如果這樣做有效,則只會處理更新後的投票數據並只有那些訂閱了這個投票的客戶端才會更新):
    • 投票數據首先由觸發poll_results服務的register_vote服務(假設這裡發生一些驗證)處理。
    • 實時匯總民意調查數據由poll_results服務中繼到前端以顯示整體統計信息。
實時投票應用程序的傳統設計
傳統設計的投票應用程序

該模型源自傳統的 API 構建方法,因此存在類似問題:

  1. 任何順序步驟都可能出錯,導致 UX 掛起並影響其他獨立操作。
  2. 需要在 API 層上付出很多努力,因為它是前端應用程序的單點接觸點,可以與多個服務進行交互。 它還需要實現基於 websockets 的實時 API——沒有通用標準,因此對工具自動化的支持有限。
  3. 前端應用程序需要添加必要的管道以使用實時 API,並且可能還必須解決通常在實時應用程序中看到的數據一致性問題(在我們選擇的示例中不太重要,但對於在現實中排序消息至關重要-時間聊天應用程序)。
  4. 許多實現求助於在服務器端(Firebase 等)使用額外的非關係數據庫來輕鬆實現實時 API 支持。

讓我們看看 GraphQL 和相關工具如何應對這些挑戰。

什麼是 GraphQL?

GraphQL 是 API 查詢語言的規範,也是用於執行查詢的服務器端運行時。 該規範由 Facebook 開發,旨在加速應用程序開發並提供標準化的、與數據庫無關的數據訪問格式。 任何符合規範的 GraphQL 服務器都必須支持以下內容:

  1. 讀取查詢
    用於從數據源(可以是數據庫、REST API 或另一個 GraphQL 模式/服務器中的一個或組合)請求嵌套數據的請求類型。
  2. 寫入的突變
    將數據寫入/中繼到上述數據源的請求類型。
  3. 實時查詢訂閱
    客戶端訂閱實時更新的請求類型。

GraphQL 還使用類型化模式。 生態系統有很多工具可以幫助您在開發/編譯時識別錯誤,從而減少運行時錯誤。

這就是 GraphQL 非常適合實時應用程序的原因:

  • 實時查詢(訂閱)是 GraphQL 規範的隱含部分。 任何 GraphQL 系統都必須具備原生實時 API 功能。
  • 實時查詢的標準規範整合了社區圍繞客戶端工具所做的努力,從而以一種非常直觀的方式與 GraphQL API 集成。

GraphQL 以及用於數據庫事件和無服務器/雲功能的開源工具的組合為構建具有異步業務邏輯和易於構建和管理的實時功能的雲原生應用程序提供了很好的基礎。 這種新範式還帶來了出色的用戶和開發人員體驗。

在本文的其餘部分,我將使用開源工具基於此架構圖構建應用程序:

基於 GraphQL 的實時投票應用程序設計
使用 GraphQL 設計的投票應用程序

構建實時投票/投票應用程序

通過對 GraphQL 的介紹,讓我們回到第一節中描述的構建投票應用程序。

選擇了三個功能(或突出顯示的故事)來演示我們的應用程序將發出的不同 GraphQL 請求類型:

  1. 詢問
    獲取投票問題及其選項。
  2. 突變
    讓用戶投票。
  3. 訂閱
    顯示投票結果的實時儀表板。
投票應用程序中的 GraphQL 元素
投票應用程序中的 GraphQL 請求類型(大預覽)

先決條件

  • Heroku 帳戶(使用免費套餐,無需信用卡)
    部署一個 GraphQL 後端(見下一點)和一個 Postgres 實例。
  • Hasura GraphQL 引擎(免費、開源)
    Postgres 上的即用型 GraphQL 服務器。
  • Apollo 客戶端(免費、開源 SDK)
    用於輕鬆地將客戶端應用程序與 GraphQL 服務器集成。
  • npm (免費的開源包管理器)
    運行我們的 React 應用程序。

部署數據庫和 GraphQL 後端

我們將在 Heroku 的免費層上分別部署一個 Postgres 和 GraphQL Engine 實例。 我們可以使用一個漂亮的 Heroku 按鈕,只需單擊一下即可。

Heroku 按鈕
Heroku 按鈕

注意:您也可以點擊此鏈接或搜索 Heroku(或其他平台)的 Hasura GraphQL 部署文檔。

將應用後端部署到 Heroku 的免費層
將 Postgres 和 GraphQL 引擎部署到 Heroku 的免費層(大預覽)

您不需要任何其他配置,只需單擊“部署應用程序”按鈕即可。 部署完成後,記下應用 URL:

 <app-name>.herokuapp.com

例如,在上面的屏幕截圖中,它將是:

 hge-realtime-app-tutorial.herokuapp.com

到目前為止,我們所做的是部署一個 Postgres 實例(作為 Heroku 術語中的一個附加組件)和一個配置為使用該 Postgres 實例的 GraphQL Engine 實例。 這樣做的結果是,我們現在有了一個現成的 GraphQL API,但是,由於我們的數據庫中沒有任何表或數據,所以這還沒有用。 所以,讓我們立即解決這個問題。

建模數據庫模式

以下架構圖為我們的投票應用程序捕獲了一個簡單的關係數據庫架構:

投票應用程序的架構設計
投票應用程序的架構設計。 (大預覽)

如您所見,該模式是一種利用外鍵約束的簡單規範化模式。 正是這些約束被 GraphQL 引擎解釋為 1:1 或 1:many 關係(例如poll:options是 1:many 關係,因為每個輪詢將有多個選項通過外鍵約束鏈接poll表的id列和option表中的poll_id列)。 相關數據可以建模為圖形,因此可以為 GraphQL API 提供動力。 這正是 GraphQL 引擎所做的。

基於上述,我們必須創建以下表和約束來對我們的模式進行建模:

  1. Poll
    用於捕獲投票問題的表格。
  2. Option
    每個投票的選項。
  3. Vote
    記錄用戶的投票。
  4. 以下字段( table : column )之間的外鍵約束:
    • option : poll_id → poll : id
    • vote : poll_id → poll : id
    • vote : created_by_user_id → user : id

現在我們已經有了架構設計,讓我們在 Postgres 數據庫中實現它。 要立即啟動此架構,我們將執行以下操作:

  1. 下載 GraphQL 引擎 CLI。
  2. 克隆這個 repo:
     $ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
  3. 轉到hasura/並編輯config.yaml
     endpoint: https://<app-name>.herokuapp.com
  4. 使用 CLI 從項目目錄中應用遷移(您剛剛通過克隆下載):
     $ hasura migrate apply

這就是後端。 您現在可以打開 GraphQL 引擎控制台並檢查所有表是否都存在(該控制台位於https://<app-name>.herokuapp.com/console )。

注意:您也可以使用控制台通過創建單個表然後使用 UI 添加約束來實現模式。 在 GraphQL 引擎中使用對遷移的內置支持只是一個方便的選項,因為我們的示例 repo 具有用於調出所需表和配置關係/約束的遷移(無論您是否正在建立一個愛好,這也是強烈推薦的)項目或生產就緒的應用程序)。

將前端 React 應用程序與 GraphQL 後端集成

本教程中的前端是一個簡單的應用程序,它在一個地方顯示投票問題、投票選項和匯總投票結果。 正如我之前提到的,我們將首先專注於運行這個應用程序,以便您立即獲得使用我們最近部署的 GraphQL API 的滿足感,看看我們在本文前面看到的 GraphQL 概念如何為此類應用程序的不同用例提供支持,然後探索 GraphQL 集成如何在幕後工作。

注意:如果你是 ReactJS 的新手,你可能想看看這些文章中的一些。 我們不會深入了解應用程序的 React 部分的細節,而是更多地關注應用程序的 GraphQL 方面。 您可以參考 repo 中的源代碼,了解有關如何構建 React 應用程序的任何詳細信息

配置前端應用程序

  1. 在上一節克隆的 repo 中,編輯src/apollo.js HASURA_GRAPHQL_ENGINE_HOSTNAME (在/community/examples/realtime-poll文件夾內)中的 HASURA_GRAPHQL_ENGINE_HOSTNAME 並將其設置為上面的 Heroku 應用程序 URL:
     export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
  2. 轉到存儲庫/應用程序文件夾的根目錄 ( /realtime-poll/ ) 並使用 npm 安裝必備模塊,然後運行應用程序:
     $ npm install $ npm start 
實時投票應用程序的屏幕截圖
實時投票應用程序的屏幕截圖(大預覽)

您現在應該可以使用該應用程序了。 繼續投票,隨心所欲,您會注意到結果實時變化。 事實上,如果您設置此 UI 的另一個實例並將其指向同一個後端,您將能夠看到所有實例的聚合結果。

那麼,這個應用程序是如何使用 GraphQL 的呢? 繼續閱讀。

幕後花絮:GraphQL

在本節中,我們將探索為應用程序提供支持的 GraphQL 功能,然後在下一節中演示易於集成。

投票組件和匯總結果圖

左上角的 poll 組件獲取包含所有選項的投票,並在數據庫中捕獲用戶的投票。 這兩個操作都是使用 GraphQL API 完成的。 為了獲取民意調查的詳細信息,我們進行了查詢(還記得 GraphQL 介紹中的這一點嗎?):

 query { poll { id question options { id text } } }

使用react-apollo中的 Mutation 組件,我們可以將突變連接到 HTML 表單,以便在提交表單時使用變量optionIduserId執行突變:

 mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }

為了顯示投票結果,我們需要從投票表中的數據中得出每個選項的投票數。 我們可以創建一個 Postgres 視圖並使用 GraphQL 引擎對其進行跟踪,以使這些派生數據在 GraphQL 上可用。

 CREATE VIEW poll_results AS SELECT poll.id AS poll_id, o.option_id, count(*) AS votes FROM (( SELECT vote.option_id, option.poll_id, option.text FROM ( vote LEFT JOIN public.option ON ((option.id = vote.option_id)))) o LEFT JOIN poll ON ((poll.id = o.poll_id))) GROUP BY poll.question, o.option_id, poll.id;

poll_results視圖連接來自votepoll表的數據,以提供每個選項的投票總數。

在這個視圖上使用 GraphQL 訂閱、react-google-charts 和react-apollo的訂閱組件,我們可以連接一個響應式圖表,該圖表在任何客戶端發生新投票時實時更新。

 subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }

GraphQL API 集成

正如我之前提到的,我使用了 Apollo Client,這是一個開源 SDK,用於將 ReactJS 應用程序與 GraphQL 後端集成。 Apollo Client 類似於任何 HTTP 客戶端庫,例如對 python 的請求、用於 JavaScript 的標準 http 模塊等。 它封裝了發出 HTTP 請求(在本例中為 POST 請求)的詳細信息。 它使用配置(在src/apollo.js中指定)來發出查詢/變異/訂閱請求(在src/GraphQL.jsx中指定,可以選擇使用可以在你的 REACT 應用程序的 JavaScript 代碼中動態替換的變量)來一個 GraphQL 端點。 它還利用 GraphQL 端點後面的類型化模式為上述請求提供編譯/開發時間驗證。 讓我們看看客戶端應用程序向 GraphQL API 發出實時查詢(訂閱)請求是多麼容易。

配置 SDK

Apollo Client SDK 需要指向 GraphQL 服務器,因此它可以自動處理這種集成通常需要的樣板代碼。 所以,這正是我們在設置前端應用程序時修改src/apollo.js時所做的。

發出 GraphQL 訂閱請求(實時查詢)

src/GraphQL.jsx文件中定義我們在上一節中查看的訂閱:

 const SUBSCRIPTION_RESULT = ` subscription getResult($pollId: uuid!) { poll_results ( order_by: option_id_desc, where: { poll_id: {_eq: $pollId} } ) { option_id option { id text } votes } }`;

我們將使用這個定義來連接我們的 React 組件:

 export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return

加載中...</p>; 如果(錯誤)返回

錯誤:</p>; 返回 ( <div> <div> {渲染圖表(數據)} </div> </div> ); }} </訂閱> )

這裡需要注意的一點是,上述訂閱也可能是一個查詢。 只需將一個關鍵字替換為另一個關鍵字就可以為我們提供“實時查詢”,這就是 Apollo Client SDK 將這個實時 API 與您的應用程序掛鉤的全部內容。 每次我們的實時查詢中有一個新的數據集時,SDK 都會使用更新的數據觸發我們的圖表的重新渲染(使用renderChart(data)調用)。 而已。 真的就是這麼簡單!

最後的想法

通過三個簡單的步驟(創建 GraphQL 後端、對應用程序模式建模以及將前端與 GraphQL API 集成),您可以快速連接一個功能齊全的實時應用程序,而不會陷入不必要的細節,例如設置一個 websocket 連接。 這就是社區工具支持像 GraphQL 這樣的抽象的力量。

如果您發現這很有趣,並希望為您的下一個項目或生產應用程序進一步探索 GraphQL,那麼您可能希望使用以下一些因素來構建您的 GraphQL 工具鏈:

  • 性能和可擴展性
    GraphQL 旨在直接由前端應用程序使用(它並不比後端的 ORM 更好;這樣做會帶來真正的生產力優勢)。 因此,您的工具需要能夠有效地使用數據庫連接,並且應該能夠輕鬆擴展。
  • 安全
    由此可見,需要一個成熟的基於角色的訪問控制系統來授權對數據的訪問。
  • 自動化
    如果您是 GraphQL 生態系統的新手,那麼手寫 GraphQL 模式和實現 GraphQL 服務器可能看起來是一項艱鉅的任務。 最大限度地提高工具的自動化程度,以便您可以專注於重要的事情,例如構建以用戶為中心的前端功能。
  • 建築學
    儘管上述工作看起來微不足道,但生產級應用程序的後端架構可能涉及高級 GraphQL 概念,如模式拼接等。此外,輕鬆生成/使用實時 API 的能力為構建異步、反應式提供了可能性具有彈性和固有可擴展性的應用程序。 因此,評估 GraphQL 工具如何簡化您的架構至關重要。

相關資源

  • 您可以在此處查看該應用程序的實時版本。
  • 完整的源代碼可在 GitHub 上獲得。
  • 如果您想探索數據庫模式並運行測試 GraphQL 查詢,可以在此處進行。