GraphQL 標誌GraphQL

分頁

不同的分頁模式啟用不同的用戶端功能

在 GraphQL 中,一個常見的用例是遍歷物件集之間的關聯。有許多不同的方式可以在 GraphQL 中公開這些關聯,為用戶端開發人員提供不同的功能集。

複數#

公開物件之間關聯最簡單的方法是使用傳回複數類型的欄位。例如,如果我們想要取得 R2-D2 朋友的清單,我們可以要求取得所有朋友

切片#

然而,我們很快就會發現用戶端可能需要其他行為。用戶端可能希望能夠指定要擷取多少朋友;也許他們只想要前兩個。因此,我們希望公開類似這樣的內容

{
hero {
name
friends(first: 2) {
name
}
}
}

但是,如果我們只擷取前兩個,我們可能也希望對清單進行分頁;一旦用戶端擷取前兩個朋友,他們可能希望傳送第二個請求來要求下兩個朋友。我們如何啟用該行為?

分頁和邊緣#

有許多方法可以進行分頁

  • 我們可以執行類似 friends(first:2 offset:2) 的動作,來要求清單中的下兩個。
  • 我們可以執行類似 friends(first:2 after:$friendId) 的動作,來要求我們擷取的最後一個朋友之後的兩個。
  • 我們可以執行類似 friends(first:2 after:$friendCursor) 的動作,其中我們從最後一項取得游標並用來分頁。

一般來說,我們發現基於游標的分頁是設計中最強大的。特別是如果游標是不透明的,則可以透過基於游標的分頁實作基於偏移量或 ID 的分頁(透過將游標設為偏移量或 ID),而且如果分頁模型在未來變更,使用游標會提供額外的彈性。為了提醒游標是不透明的,而且不應依賴其格式,我們建議對其進行 base64 編碼。

這會導致一個問題;但是,我們如何從物件取得游標?我們不希望游標存在於 User 類型中;它是連線的屬性,而非物件的屬性。因此我們可能想要引入一個新的間接層級;我們的 friends 欄位應提供邊緣清單,而邊緣同時具有游標和基礎節點

{
hero {
name
friends(first: 2) {
edges {
node {
name
}
cursor
}
}
}
}

如果資訊是特定於邊緣,而非特定於其中一個物件,邊緣的概念也會很有用。例如,如果我們想要在 API 中公開「友誼時間」,讓它存在於邊緣是一個很自然的地方。

清單結束、計數和連線#

現在我們有能力使用游標對連線進行分頁,但是我們如何知道何時到達連線的尾端?我們必須持續查詢,直到我們取得一個空清單,但我們真的很希望連線在我們到達尾端時告訴我們,這樣我們就不需要額外的請求。類似地,如果我們想要知道關於連線本身的其他資訊;例如,R2-D2 總共有多少朋友?

為了解決這兩個問題,我們的 friends 欄位可以傳回一個連線物件。然後連線物件將有一個欄位用於邊緣,以及其他資訊(例如總計數和關於是否存在下一頁的資訊)。因此,我們的最後查詢可能看起來更像

{
hero {
name
friends(first: 2) {
totalCount
edges {
node {
name
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}

請注意,我們也可能在這個 PageInfo 物件中包含 endCursorstartCursor。這樣一來,如果我們不需要邊緣包含的任何其他資訊,我們就完全不需要查詢邊緣,因為我們從 pageInfo 取得了分頁所需的游標。這會為連線帶來潛在的可使用性改善;我們不僅公開 edges 清單,我們還可以公開僅包含節點的專用清單,以避免間接層。

完整的連線模型#

很明顯地,這比我們原本僅有一個複數的設計更為複雜!但是,透過採用這個設計,我們已經為客戶端解鎖了許多功能

  • 在清單中分頁的能力。
  • 要求連線本身的資訊,例如 totalCountpageInfo 的能力。
  • 要求邊緣本身的資訊,例如 cursorfriendshipTime 的能力。
  • 由於使用者只使用不透明游標,因此能夠變更後端執行分頁的方式。

若要實際操作,範例架構中有一個額外的欄位稱為 friendsConnection,可顯示所有這些概念。您可以在範例查詢中查看。嘗試移除 friendsConnectionafter 參數,看看分頁會如何受到影響。此外,嘗試在連線中將 edges 欄位替換為輔助的 friends 欄位,當這對客戶端合適時,讓您可以直接取得朋友清單,而不需要額外的邊緣間接層。

連線規格#

為了確保此模式的一致實作,Relay 專案有一個正式的 規格,您可以遵循此規格來建置使用基於游標的連線模式的 GraphQL API。

繼續閱讀 →全域物件識別