跳至主要內容
版本:v18.0.0

GraphQL 伺服器規格

本文檔的目標是指定 Relay 對於 GraphQL 伺服器的假設,並透過一個 GraphQL 綱要範例來展示這些假設。

目錄

前言

Relay 對於 GraphQL 伺服器的兩個核心假設是,它提供:

  1. 重新提取物件的機制。
  2. 如何分頁瀏覽連線的說明。

這個範例展示了這兩個假設。這個範例並非全面性的,但旨在快速介紹這些核心假設,以便在深入了解程式庫的更詳細規格之前提供一些背景知識。

這個範例的前提是,我們想要使用 GraphQL 查詢關於原始星際大戰三部曲中艦艇和派系的資訊。

假設讀者已經熟悉 GraphQL;如果沒有,GraphQL.js 的 README 是個好的起點。

也假設讀者已經熟悉 星際大戰;如果沒有,1977 年版的星際大戰是個好的起點,雖然 1997 年的特別版也適用於本文檔的目的。

綱要

以下描述的綱要將用於展示 Relay 使用的 GraphQL 伺服器應實作的功能。兩個核心類型是星際大戰宇宙中的派系和艦艇,其中一個派系有很多與之相關聯的艦艇。

interface Node {
id: ID!
}

type Faction implements Node {
id: ID!
name: String
ships: ShipConnection
}

type Ship implements Node {
id: ID!
name: String
}

type ShipConnection {
edges: [ShipEdge]
pageInfo: PageInfo!
}

type ShipEdge {
cursor: String!
node: Ship
}

type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}

type Query {
rebels: Faction
empire: Faction
node(id: ID!): Node
}

物件識別

FactionShip 都有可用於重新提取它們的識別碼。我們透過 Node 介面和根查詢類型上的 node 欄位向 Relay 公開此功能。

Node 介面包含一個欄位 id,它是 ID!node 根欄位接受一個參數,即 ID!,並返回一個 Node。這兩者協同工作以允許重新提取;如果我們將該欄位中返回的 id 傳遞給 node 欄位,我們會取回該物件。

讓我們看看實際情況,並查詢反抗軍的 ID

query RebelsQuery {
rebels {
id
name
}
}

返回

{
"rebels": {
"id": "RmFjdGlvbjox",
"name": "Alliance to Restore the Republic"
}
}

現在我們知道反抗軍在我們系統中的 ID。我們現在可以重新提取它們

query RebelsRefetchQuery {
node(id: "RmFjdGlvbjox") {
id
... on Faction {
name
}
}
}

返回

{
"node": {
"id": "RmFjdGlvbjox",
"name": "Alliance to Restore the Republic"
}
}

如果我們對帝國執行相同的操作,我們會發現它返回不同的 ID,我們也可以重新提取它

query EmpireQuery {
empire {
id
name
}
}

產生

{
"empire": {
"id": "RmFjdGlvbjoy",
"name": "Galactic Empire"
}
}

query EmpireRefetchQuery {
node(id: "RmFjdGlvbjoy") {
id
... on Faction {
name
}
}
}

產生

{
"node": {
"id": "RmFjdGlvbjoy",
"name": "Galactic Empire"
}
}

Node 介面和 node 欄位假設使用全域唯一 ID 來進行重新提取。沒有全域唯一 ID 的系統通常可以透過將類型與類型特定的 ID 組合來合成它們,這也是此範例中所做的事情。

我們取回的 ID 是 base64 字串。ID 的設計目的是不透明的(唯一應該傳遞給 node 上的 id 參數的是查詢系統中某個物件上的 id 的未經更改的結果),並且 base64 字串是 GraphQL 中一個有用的慣例,以提醒查看者該字串是不透明的識別碼。

關於伺服器應如何運作的完整詳細資訊,請參閱 GraphQL 網站上的 GraphQL 物件識別最佳實務指南。

連線

一個派系在星際大戰宇宙中有很多艦艇。Relay 包含一些功能,可以使用標準化的方式來表示這些一對多關係,從而輕鬆操作一對多關係。這種標準連線模型提供了切片和分頁瀏覽連線的方法。

讓我們以反抗軍為例,並要求他們的第一艘艦艇

query RebelsShipsQuery {
rebels {
name
ships(first: 1) {
edges {
node {
name
}
}
}
}
}

產生

{
"rebels": {
"name": "Alliance to Restore the Republic",
"ships": {
"edges": [
{
"node": {
"name": "X-Wing"
}
}
]
}
}
}

這使用了 shipsfirst 參數將結果集切片到第一個。但是,如果我們想要分頁瀏覽它呢?在每個邊緣上,都會公開一個可以用於分頁的游標。讓我們這次要求前兩個,並同時取得游標

query MoreRebelShipsQuery {
rebels {
name
ships(first: 2) {
edges {
cursor
node {
name
}
}
}
}
}

我們取回


{
"rebels": {
"name": "Alliance to Restore the Republic",
"ships": {
"edges": [
{
"cursor": "YXJyYXljb25uZWN0aW9uOjA=",
"node": {
"name": "X-Wing"
}
},
{
"cursor": "YXJyYXljb25uZWN0aW9uOjE=",
"node": {
"name": "Y-Wing"
}
}
]
}
}
}

請注意,游標是 base64 字串。這是先前的模式:伺服器正在提醒我們這是一個不透明的字串。我們可以將此字串作為 after 參數傳遞回伺服器,傳遞給 ships 欄位,這將允許我們要求前一個結果中最後一個之後的下三艘艦艇


query EndOfRebelShipsQuery {
rebels {
name
ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") {
edges {
cursor
node {
name
}
}
}
}
}

給我們



{
"rebels": {
"name": "Alliance to Restore the Republic",
"ships": {
"edges": [
{
"cursor": "YXJyYXljb25uZWN0aW9uOjI=",
"node": {
"name": "A-Wing"
}
},
{
"cursor": "YXJyYXljb25uZWN0aW9uOjM=",
"node": {
"name": "Millennium Falcon"
}
},
{
"cursor": "YXJyYXljb25uZWN0aW9uOjQ=",
"node": {
"name": "Home One"
}
}
]
}
}
}

太棒了!讓我們繼續,取得接下來的四艘!

query RebelsQuery {
rebels {
name
ships(first: 4 after: "YXJyYXljb25uZWN0aW9uOjQ=") {
edges {
cursor
node {
name
}
}
}
}
}

產生

{
"rebels": {
"name": "Alliance to Restore the Republic",
"ships": {
"edges": []
}
}
}

嗯。沒有其他艦艇了;猜測反抗軍系統中只有五艘。如果我們在不必執行另一次往返行程來驗證的情況下,就能知道我們已到達連線的末尾,那就太好了。連線模型透過一個稱為 PageInfo 的類型公開此功能。因此,讓我們再次發出之前取得艦艇的兩個查詢,但這次要求 hasNextPage

query EndOfRebelShipsQuery {
rebels {
name
originalShips: ships(first: 2) {
edges {
node {
name
}
}
pageInfo {
hasNextPage
}
}
moreShips: ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") {
edges {
node {
name
}
}
pageInfo {
hasNextPage
}
}
}
}

我們取回

{
"rebels": {
"name": "Alliance to Restore the Republic",
"originalShips": {
"edges": [
{
"node": {
"name": "X-Wing"
}
},
{
"node": {
"name": "Y-Wing"
}
}
],
"pageInfo": {
"hasNextPage": true
}
},
"moreShips": {
"edges": [
{
"node": {
"name": "A-Wing"
}
},
{
"node": {
"name": "Millennium Falcon"
}
},
{
"node": {
"name": "Home One"
}
}
],
"pageInfo": {
"hasNextPage": false
}
}
}
}

因此,在第一個艦艇查詢中,GraphQL 告訴我們有下一頁,但在下一個查詢中,它告訴我們已經到達連線的末尾。

Relay 使用所有這些功能來建構關於連線的抽象概念,以便輕鬆有效地使用它們,而無需在用戶端上手動管理游標。

關於伺服器應如何運作的完整詳細資訊,請參閱 GraphQL 游標連線規格。

延伸閱讀

這總結了 GraphQL 伺服器規格的概述。有關符合 Relay 的 GraphQL 伺服器的詳細要求,請參閱關於 Relay 游標連線模型的更正式說明,以及 GraphQL 全域物件識別模型。

若要查看實作規格的程式碼,GraphQL.js Relay 程式庫提供了用於建立節點和連線的輔助函式;該儲存庫的 __tests__ 資料夾包含上述範例的實作,作為儲存庫的整合測試。


這個頁面有幫助嗎?

請透過以下方式協助我們讓網站變得更好 回答幾個快速問題.