理解 GraphQL 類型系統

作者選擇了自由和開放源代碼基金作為Write for DOnations計劃的一部分進行捐贈。

介紹

GraphQL是促進前端與數據源通信的現代解決方案。所有關於GraphQL實現的細節和能力都在GraphQL Schema中明確列出。為了編寫一個有效的GraphQL模式,您必須理解GraphQL類型系統

在本文中,您將了解GraphQL類型:五種內置的標量類型、枚舉列表和非空包裹類型、對象類型,以及與它們一起工作的抽象接口聯合類型。您將查看每種類型的示例,並學習如何使用它們來構建完整的GraphQL模式。

先決條件

為了充分利用本教程,您應該具備:

純量類型

GraphQL 模式中的所有數據最終都解析為不同的 純量 類型,這些類型代表原始值。 GraphQL 響應可以表示為樹,而純量類型是樹末端的葉子。 嵌套響應可以有多個級別,但最後一級始終解析為純量(或枚舉)類型。 GraphQL 預設提供了五種內置的純量類型:IntFloatStringBooleanID

Int

Int 是一個帶符號的32位非小數數值。它是一個帶符號(正或負)的整數,不包括小數部分。帶符號32位整數的最大值是 2,147,483,647。這是用於數值數據的兩種內置純量之一。

Float

A Float is a signed double-precision fractional value. It is a signed (positive or negative) number that contains a decimal point, such as 1.2. This is the other built-in scalar used for numerical data.

String

A String is a UTF-8 character sequence. The String type is used for any textual data. This can also include data like very large numbers. Most custom scalars will be types of string data.

Boolean

A Boolean is a true or false value.

ID

一個ID是一個唯一的標識符。該值始終被序列化為字符串,即使ID是數值。ID類型可能通常用通用唯一標識符(UUID)來表示。

自訂標量

除了這些內建的標量之外,標量 關鍵字可以用來定義自訂標量。您可以使用自訂標量來創建具有額外伺服器級驗證的類型,例如 日期時間網址。以下是定義新 日期 類型的示例:

scalar Date

伺服器將知道如何使用 GraphQLScalarType 處理與此新類型的互動。

列舉類型

列舉 類型,又稱為 列舉器 類型,描述一組可能的值。

使用來自 如何使用 GraphQL 管理資料 系列其他教程中的幻想遊戲 API 主題,您可以為遊戲角色的 職業物種 創建一個包含系統接受的所有值的 enum。列舉型別使用 enum 關鍵字定義,如下所示:

"The job class of the character."
enum Job {
  FIGHTER
  WIZARD
}

"The species or ancestry of the character."
enum Species {
  HUMAN
  ELF
  DWARF
}

在这种方式下,确保角色的JobFIGHTERWIZARD,永远不会意外地变成"purple"或其他随机字符串,如果使用String类型而不是自定义的枚举类型可能会发生。枚举类型通常按照惯例使用全大写字母编写。

枚举类型还可以用作参数的接受值。例如,您可以创建一个Hand enum来表示武器是单手的(如短剑)还是双手的(如重斧),并使用它来确定是否可以装备一个或两个:

enum Hand {
  SINGLE
  DOUBLE
}

"A valiant weapon wielded by a fighter."
type Weapon {
  name: String!
  attack: Int
  range: Int
  hand: Hand
}

type Query {
  weapons(hand: Hand = SINGLE): [Weapon]
}

已使用SINGLEDOUBLE作为Hand enum的值,并且weapons字段上的参数具有SINGLE的默认值,这意味着如果没有传递参数,它将回退为SINGLE

非空类型

您可能注意到nullundefined,许多语言视为原始类型的常见类型,在内置标量列表中是缺失的。 Null 在GraphQL中存在,表示缺乏值。

所有GraphQL中的類型都默認可為空,因此null對於任何類型都是有效的響應。為了使值成為必需,必須將其轉換為帶有尾部感歎號的GraphQL非空類型。非空被定義為類型修飾符,這些類型用於修改它所引用的類型。例如,String是可選(或可為空)字符串,而String!是必需(或非空)字符串。

列表類型

A List type in GraphQL is another type modifier. Any type that is wrapped in square brackets ([]) becomes a List type, which is a collection that defines the type of each item in a list.

例如,定義為[Int]的類型將是Int類型的集合,而[String]將是String類型的集合。非空和列表可以一起使用,使類型既是必需的又定義為列表,例如[String]!

對象類型

如果GraphQL純量類型描述了層次化GraphQL響應末端的“葉子”,則對象類型描述了中介的“分支”,幾乎所有GraphQL模式中的內容都是對象類型。

對象由一系列命名字段(鍵)和每個字段將解析為的值類型組成。對象使用type 關鍵字定義。必須定義至少一個或多個字段,且字段不能以兩個下劃線(__)開頭,以避免與GraphQL內省系統衝突。

在GraphQL Fantasy Game API示例中,您可以創建一個Fighter對象來表示遊戲中的一種角色類型:

"A hero with direct combat ability and strength."
type Fighter {
  id: ID!
  name: String!
  level: Int
  active: Boolean!
}

在這個例子中,Fighter對象類型已經聲明,並且它有四個命名字段:

  • id 產生一個非空的ID類型。
  • name 產生一個非空的String類型。
  • level 產生一個Int類型。
  • active 產生一個非空的Boolean類型。

在聲明之上,您還可以使用雙引號添加評論,就像這個例子中的"A hero with direct combat ability and strength."一樣。這將顯示為類型的描述。

在這個例子中,每個字段解析為純量類型,但對象字段也可以解析為其他對象類型。例如,您可以創建一個Weapon類型,並且GraphQL模式可以設置,其中Fighter上的weapon字段將解析為Weapon對象:

"A valiant weapon wielded by a fighter."
type Weapon {
  name: String!
  attack: Int
  range: Int
}

"A hero with direct combat ability and strength."
type Fighter {
  id: ID!
  name: String!
  level: Int
  active: Boolean!
  weapon: Weapon
}

對象也可以嵌套到其他對象的字段中。

根操作類型

有三種特殊的物件充當 GraphQL 架構的入口點:Query、Mutation 和 Subscription。這些被稱為根操作類型,並遵從任何其他物件類型的相同規則。

schema 關鍵字表示進入 GraphQL 架構的入口點。您的根 Query、Mutation 和 Subscription 類型將位於根 schema 物件中:

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

Query 類型是任何 GraphQL 架構中必需的,代表一個讀取請求,類似於 REST API 中的 GET。以下是一個根 Query 物件的示例,它返回一個 Fighter 類型的列表:

type Query {
  fighters: [Fighter]
}

Mutations 代表一個寫入請求,類似於 REST API 中的 POSTPUTDELETE。在以下示例中,Mutation 具有一個帶有命名參數(input)的 addFighter 字段:

type Mutation {
  addFighter(input: FighterInput): Fighter
}

最後,Subscription 對應到一個事件流,可以與 Web 應用程式中的 Websocket 一起使用。在 GraphQL Fantasy API 中,也許可以用於隨機戰鬥遭遇,如下所示:

type Subscription {
  randomBattle(enemy: Enemy): BattleResult
}

請注意,schema 入口點在某些 GraphQL 實現中通常被抽象化。

字段參數

GraphQL 物件的字段本質上是返回值的函數,它們可以像任何函數一樣接受參數。 字段參數由參數名稱和類型組成。參數可以是任何非物件類型。在此示例中,Fighter 物件可以通過 id 字段進行篩選(解析為非空 ID 類型):

type Query {
  fighter(id: ID!): Fighter
}

這個特定的示例用於從數據存儲中提取單個項目,但參數也可以用於篩選、分頁和其他更具體的查詢。

介面類型

與物件類型一樣,抽象的 介面類型由一組命名字段及其相關的值類型組成。介面看起來和遵循與物件相同的所有規則,但用於定義物件實現的子集。

到目前為止,在你的架構中,你有一個Fighter物件,但你可能還想要建立一個Wizard、一個Healer,以及其他一些物件,它們將共享許多相同的欄位,但也會有一些差異。在這種情況下,你可以使用介面來定義它們共同擁有的欄位,並創建實現該介面的物件。

在以下示例中,你可以使用interface關鍵字創建一個BaseCharacter介面,其中包含每種類型角色都擁有的所有欄位:

"A hero on a quest."
interface BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job
}

每種角色類型都將具有欄位idnamelevelspeciesjob

現在,假設你有一個Fighter類型和一個Wizard類型,它們具有這些共享欄位,但Fighters使用Weapon,而Wizards使用Spells。你可以使用implements關鍵字將每個都標記為BaseCharacter的實現,這意味著它們必須具有從所創建的介面繼承的所有欄位:

"A hero with direct combat ability and strength."
type Fighter implements BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job!
  weapon: Weapon
}

"A hero with a variety of magical powers."
type Wizard implements BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job!
  spells: [Spell]
}

FighterWizard都是BaseCharacter介面的有效實現,因為它們具有所需的欄位子集。

Union Type

另一個可以與對象一起使用的抽象類型是 Union 類型。使用 union 關鍵字,您可以定義一個包含一組對象的類型,這些對象都可以作為響應的有效值。

使用上一節中創建的接口,您可以創建一個 Character Union,將字符定義為 WizardFighter

union Character = Wizard | Fighter

等號字符(=)設置定義,管道字符(|)作為 OR 聲明。請注意,聯合必須由對象或接口組成。純量類型在聯合中無效。

現在,如果您查詢字符列表,它可以使用 Character 聯合並返回所有 WizardFighter 類型。

結論

在這個教程中,你學到了許多定義 GraphQL 類型系統 的類型。最基本的類型是標量類型,它們是模式樹上的葉子,包括 IntFloatStringBooleanID,以及 GraphQL 實現決定創建的任何自定義標量。列舉是有效常數值的列表,當你需要對響應進行更多控制而不僅僅宣告它為 String 時可以使用,它們也是模式樹上的葉子。列表和非空類型被稱為類型修改器,或包裝類型,它們可以定義其他類型作為集合或必需的。對象是模式樹的分支,幾乎在 GraphQL 模式中的每件事物都是對象的一種類型,包括 querymutationsubscription 入口點。接口和聯合類型是抽象類型,對於定義對象很有幫助。

為了進一步學習,你可以通過閱讀 在 Node.js 中建立 GraphQL API 服務器的方法 教程,瞭解創建和修改 GraphQL 模式的實踐,以建立一個可運行的 GraphQL 服務器環境。

Source:
https://www.digitalocean.com/community/conceptual-articles/understanding-the-graphql-type-system