理解 GraphQL 类型系统

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 笔记

3.pngGraphQL 最初于 2012 年在 Facebook 开发,作为针对动力不足的移动设备的更好的数据获取解决方案,GraphQL 于 2015 年开源。作为一种为灵活性而设计的 API 技术,GraphQL 是 API 的开发人员和消费者以及他们背后的组织的强大推动者。GraphQL 实现的所有细节和功能都在 GraphQL Schema 中列出。为了编写一个有效的 GraphQL schema,必须理解好 GraphQL 类型系统

在本文中,将学习 GraphQL 类型:五种内置标量(scalar)类型、枚举(enums)、列表(list)和非空包装(non-null)类型、对象(object)类型以及与它们一起工作的抽象接口和联合类型(union)。


标量类型


GraphQL 模式中的所有数据最终都解析为各种标量类型,代表原始值。GraphQL 响应可以看作一棵树,标量类型是树末端的叶子。嵌套响应中可以有多个级别,但最后一个级别将始终解析为标量(或枚举)类型。 GraphQL 带有五种内置标量类型:IntFloatStringBooleanID

Int

Int 是带符号的 32 位非小数值,它是不包括小数的带符号(正或负)整数。带符号的 32 位整数的最大值为 2,147,483,647。这是用于数值数据的两个内置标量之一。

Float

Float 是带符号的双精度小数值。它是一个带小数点的有符号(正或负)数,例如 1.2,这是用于数值数据的另一个内置标量。

String

StringUTF-8 字符序列。 String 类型用于任何文本数据,这也可以包括非常大的数字等数据。大多数自定义标量都是字符串数据类型。

Boolean

Boolean 包含 truefalse

ID

ID 是唯一标识符,始终序列化为字符串,即使 ID 是数字也是如此。ID 类型通常可以用通用唯一标识符 (UUID) 表示。

自定义标量

除了上述这些内置标量之外,还可以使用 scalar 关键字来定义自定义标量。可以使用自定义标量来创建具有额外服务器级别验证的类型,例如 DateTimeUrl。下面是一个定义新 Date 类型的示例:

scalar Date

服务器将知道如何使用 GraphQLScalarType 处理与这种新类型的交互。


枚举(Enum)类型


Enum 类型,也称为 Enumerator 类型,用于描述了一组可能的值。

例如可以为游戏角色的 JobSpecies 创建一个枚举,其中包含系统将接受的所有值。

"角色的工作等级"
enum Job {
  FIGHTER
  WIZARD
}
"性格的种类或血统"
enum Species {
  HUMAN
  ELF
  DWARF
}

通过定义枚举类型可以保证角色的 Job 只能是 FIGHTERWIZARD,并且永远不会意外地成为其他一些随机字符串,如果使用 String 类型而不是 Enum,那么就有可能是别的随机字符串。

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

enum Hand {
  SINGLE
  DOUBLE
}
"战士使用的一种武器"
type Weapon {
  name: String!
  attack: Int
  range: Int
  hand: Hand
}
type Query {
  weapons(hand: Hand = SINGLE): [Weapon]
}

Hand 枚举已声明为 SINGLEDOUBLE 作为值,weapons 字段上的参数具有默认值 SINGLE,这意味着如果未传递任何参数,它将回默认为 SINGLE


非空类型


可能会注意到内置标量列表中缺少 nullundefined(一种被许多语言视为原始类型的常见类型)。 Null 在 GraphQL 中确实存在,表示缺少一个值。默认情况下,GraphQL 中的所有类型都可以为 null,因此 null 是对任何类型的有效响应。为了使值成为必需值,必须将其转换为带有尾随感叹号的 GraphQL 非空类型。 Non-Null 被定义为类型修饰符,这些类型用于修饰它所引用的类型。例如,String 是一个可选的(或可为空的)字符串,而 String! 是必需的(或非空的)字符串。


列表类型


GraphQL 中的 List 类型是另一种类型修饰符。任何用方括号 ([]) 括起来的类型都会成为 List 类型,这是一个定义列表中每个项目类型的集合,像 JavaScript 中的数组。

例如,定义为 [Int] 的类型意味着这个集合所有元素的类型为 Int 类型,[String] 将是 String 类型的集合。 Non-NullList 可以一起使用,使一个类型既需要又定义为 List,例如 [String]!


对象类型


如果 GraphQL 标量类型描述分层 GraphQL 响应末尾的“叶子”,那么对象类型描述中间的 分支,并且 GraphQL 模式中的几乎所有内容都是一种对象类型。

对象由命名字段(键)列表和每个字段将解析为的值类型组成。对象是用 type 关键字定义的。至少要定义一个或多个字段,字段不能以两个下划线(__)开头,以免与GraphQL自省系统冲突。

例如创建一个 Fighter 对象来表示游戏中的一种角色:

"具有直接战斗能力和力量的英雄"
type Fighter {
  id: ID!
  name: String!
  level: Int
  active: Boolean!
}

在此示例中,声明了 Fighter 对象类型,定义了 4 个字段:

  • id:非空 ID 类型。
  • name:非空字符串类型。
  • levelInt 类型。
  • active:非空布尔类型。

在声明上方,可以使用双引号添加注释,如本例:具有直接战斗能力和力量的英雄,这将显示为类型的描述。

在此示例中,每个字段都解析为标量类型,但对象字段也可以解析为其他对象类型。例如,可以创建一个 Weapon 类型,并且可以设置 GraphQL 模式,其中 Fighter 上的 weapon 字段将解析为一个 Weapon 对象:

"战士使用的一种武器"
type Weapon {
  name: String!
  attack: Int
  range: Int
}
"具有直接战斗能力和力量的英雄"
type Fighter {
  id: ID!
  name: String!
  level: Int
  active: Boolean!
  weapon: Weapon
}

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


根操作类型

有三种特殊对象作为 GraphQL schema 的入口点:QueryMutationSubcription。这些被称为根操作类型,并遵循与任何其他对象类型相同的规则。

schema 关键字表示 GraphQL 模式的入口点。根 QueryMutationSubcription 类型将位于根模式对象上:

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

Query 类型在任何 GraphQL 模式上都是必需的,代表一个读取请求,类似于 REST API GET。以下是返回 Fighter 类型列表的根查询对象的示例:

type Query {
  fighters: [Fighter]
}

Mutations 代表写入请求,类似于 REST API 中的 POSTPUTDELETE。在以下示例中,Mutation 有一个带有命名参数(输入)的 addFighter 字段:

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

最后,一个 Subscription 对应于一个事件流,它将与 Web 应用程序中的 Websocket 结合使用。如下所示:

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

请注意,schema 入口点通常在某些 GraphQL 实现中被抽象掉。

字段参数

GraphQL 对象的字段本质上是返回值的函数,并且它们可以像任何函数一样接受参数。字段参数由参数名称后跟类型定义,参数可以是任何非对象类型。在此示例中,可以通过 id 字段(解析为非空 ID 类型)过滤 Fighter 对象:

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

这个特定示例对于从数据存储中获取单个项目很有用,但参数也可用于过滤、分页和其他更具体的查询。


接口类型


Object 类型一样,抽象接口类型由一系列命名字段及其关联的值类型组成。接口看起来像并遵循与对象相同的所有规则,但用于定义对象实现的子集。

到目前为止,在 schema 中有一个 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 使用 WeaponWizards 使用 Spells。可以使用 implements 关键字将每个描述为 BaseCharacter 实现,这意味着它们必须具有创建的接口中的所有字段:

type Fighter implements BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job!
  weapon: Weapon
}
type Wizard implements BaseCharacter {
  id: ID!
  name: String!
  level: Int!
  species: Species
  job: Job!
  spells: [Spell]
}

FighterWizard 都是 BaseCharacter 接口的有效实现,因为它们具有所需的字段子集。


Union 类型


可以与对象一起使用的另一种抽象类型是 union 类型。使用 union 关键字,可以定义一个类型,其中包含所有有效响应的对象列表。

使用上面创建的接口,可以创建一个 Character union,将 character 定义为 WizardFighter

union Character = Wizard | Fighter

等号 = 设置定义,管道符 | 用作 OR 语句。请注意,union 必须由对象或接口组成,标量类型在 union 上无效。

现在,如果查询 characters 列表,它可以使用 Characterunion 并返回所有 WizardFighter 类型。


总结


上面学习了定义 GraphQL 类型系统的类型,包括最基本的类型是标量类型由 IntFloatStringBooleanID和 GraphQL 实现创建的任何自定义标量类型组成。枚举是有效常量值的列表,当需要对查询响应进行更多控制时,可以使用枚举,而不是简单地将其声明为字符串。列表类型和非空类型被称为类型修饰符 type modifier 或包装类型 wrapping type,它们分别可以将其他类型定义为集合类型或必需类型。GraphQL schema 中的几乎所有内容都是对象类型,包括 querymutationsubscription 入口点。接口和联合类型是抽象类型,在定义对象时很有用。



相关文章
|
5月前
|
XML 安全 程序员
了解编程语言中的类型安全
【7月更文挑战第15天】本文介绍类型安全与效率在语言设计中至关重要。安全性需求各异,从HIPAA到PCI合规,选择最佳技术栈要考虑多方面,包括微服务架构中的语言多样性,以确保每个服务都能高效且安全地执行其特定任务。
159 14
了解编程语言中的类型安全
|
5月前
|
人工智能 Java C#
理解编程语言的类型系统
【7月更文挑战第11天】本文介绍现代软件工程借助形式化方法确保系统正确性。强类型系统如数理逻辑虽能表达普遍属性但复杂,轻量级技术如模型检查(用于芯片设计和通信协议验证)和类型系统则更易用。类型系统是推理工具,起源于解决逻辑悖论,经历多个里程碑发展,分为动态和静态分支,重点在于编程语言应用,特别是AI领域。
70 2
理解编程语言的类型系统
|
6月前
|
前端开发 JavaScript 安全
TypeScript作为一种静态类型的JavaScript超集,其强大的类型系统和面向对象编程特性为微前端架构的实现提供了有力的支持
【6月更文挑战第11天】微前端架构借助TypeScript提升开发效率和代码可靠性。 TypeScript提供类型安全,防止微前端间通信出错;智能提示和自动补全加速跨代码库开发;重构支持简化代码更新。通过定义公共接口确保一致性,用TypeScript编写微前端以保证质量。集成到构建流程确保顺利构建打包。在微前端场景中,TypeScript是强有力的语言选择。
50 2
|
5月前
|
XML 安全 程序员
理解不同编程语言类型安全的需求
【7月更文挑战第12天】本文介绍编程语言类型系统与语言设计密不可分,影响编程习惯和语言安全。微服务架构允许跨语言技术栈,每个服务都能用最适合的平台构建,强调代码质量原则,以实现高效解决方案。
75 0
|
6月前
|
开发框架 JavaScript 前端开发
TypeScript是一种高效的编程语言
【6月更文挑战第1天】TypeScript是一种高效的编程语言
48 6
|
7月前
|
Rust 安全 Java
Rust 语言的类型系统
假如让你设计编程语言的类型,你会怎么做? 要定义哪些类型? 类型之间如何交互? 是否需要类型推断? 类型系统是编程语言中用于定义和控制类型的一组规则。
Rust 语言的类型系统
|
7月前
|
缓存 JavaScript 前端开发
【TypeScript技术专栏】TypeScript与GraphQL数据交互
【4月更文挑战第30天】本文探讨了在TypeScript中使用GraphQL进行数据交互的优势和方法。GraphQL作为一种API查询语言,解决了REST API的过度获取和欠获取问题,允许客户端按需请求数据。结合TypeScript,可以构建强类型、高效的前端数据层。文章涵盖了GraphQL简介、为何选择GraphQL、在TypeScript中的使用、查询和变异、相关工具库以及最佳实践。通过案例分析,展示了GitHub、Shopify和The New York Times等大公司如何成功应用这一技术组合。学习并掌握GraphQL和TypeScript将提升开发者的技术能力和项目效率。
60 0
|
API
GraphQL
GraphQL
65 0
|
JavaScript 前端开发 编译器
Typescript vs Javascript 这两种流行编程语言之间的主要区别
TypeScript 是 JavaScript 的一个超集,它添加了静态类型、类、接口和其他一些特性。下面是 TypeScript 和 JavaScript 之间的主要区别:
105 0
|
SQL 缓存 测试技术
GraphQL 是一个陷阱?
本文最初发布于Marc-André Giroux博客,由 InfoQ 中文站翻译并分享。
173 0
GraphQL 是一个陷阱?