GraphQL: 自省(introspection)
本篇文章是根据官方网站”Introspection”提供的信息,对GraphQL的introspection机制进行的解释。这篇文章不是文档的翻译,而是用日语重新解释了原文。有一些省略的部分,同时也补充了一些原文中不太容易理解的内容。需要注意的是,GraphQL官方网站的代码示例是一个交互环境,你可以重新编写代码并验证结果,欢迎尝试一下。
有时候,了解GraphQL模式支持的查询类型是非常有用的。在GraphQL中,可以通过使用反射机制来获取这些信息。
在《星球大战》的例子中,文件starWarsIntrospection-test.ts包含了许多查询,用于展示内省系统。这是一个测试文件,用于执行参考已实现的内省系统。
类型系统的设计者了解可以使用哪些类型。即使没有了解,他们可以随时查询根查询类型的_ _ schema字段,并向GraphQL提问。那么,让我们问一下可以使用哪些类型。
{
__schema {
types {
name
}
}
}
{
"data": {
"__schema": {
"types": [
{
"name": "Query"
},
{
"name": "String"
},
{
"name": "ID"
},
{
"name": "Mutation"
},
{
"name": "Episode"
},
{
"name": "Character"
},
{
"name": "Int"
},
{
"name": "LengthUnit"
},
{
"name": "Human"
},
{
"name": "Float"
},
{
"name": "Droid"
},
{
"name": "FriendsConnection"
},
{
"name": "FriendsEdge"
},
{
"name": "PageInfo"
},
{
"name": "Boolean"
},
{
"name": "Review"
},
{
"name": "ReviewInput"
},
{
"name": "Starship"
},
{
"name": "SearchResult"
},
{
"name": "__Schema"
},
{
"name": "__Type"
},
{
"name": "__TypeKind"
},
{
"name": "__Field"
},
{
"name": "__InputValue"
},
{
"name": "__EnumValue"
},
{
"name": "__Directive"
},
{
"name": "__DirectiveLocation"
}
]
}
}
}
有相当数量的型。我们将其分为三类,进行说明。
Query/Character/Human/Episode/Droid – 型システムで定義された型。
String/Boolean – 型システムに備わる組み込みのスカラー。
__Schema/__Type/__TypeKind/__Field/__InputValue/__EnumValue/__Directive – 先頭のふたつのアンダースコア(__)は、イントロスペクションシステムの一部であることを表す。
让我们从调查有哪些查询开始吧。在设计类型系统时,已指定了所有查询的起始类型。我们可以尝试询问一下内省系统。
{
__schema {
queryType {
name
}
}
}
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
}
}
}
}
这个结果与之前在文章「GraphQL: Schema and Types」中关于类型系统的讲解一致,我们提到了Query类型是入口点(请参阅「Query类型和Mutation类型」)。Query类型仅仅是一种惯例上的命名。你可以给它取任何其他的名字,只要指定为查询的起始类型即可返回结果。
在特定情况下,你可能想要确认一些事情。例如,如果是Droid类型的情况。
{
__type(name: "Droid") {
name
}
}
{
"data": {
"__type": {
"name": "Droid"
}
}
}
既然在查询参数中传递了name,那么值就已经知道了。如果有其他想知道的事情,比如想确认是接口还是对象。
{
__type(name: "Droid") {
name
kind
}
}
{
"data": {
"__type": {
"name": "Droid",
"kind": "OBJECT"
}
}
}
kind返回TypeKind枚举类型。OBJECT是其值之一。但如果询问Character,就会发现它是一个接口(INTERFACE)。
{
__type(name: "Character") {
name
kind
}
}
{
"data": {
"__type": {
"name": "Character",
"kind": "INTERFACE"
}
}
}
了解一个对象具有哪些字段将会很有用。我们可以通过内省系统询问关于Droid的信息。
{
__type(name: "Droid") {
name
fields {
name
type {
name
kind
}
}
}
}
{
"data": {
"__type": {
"name": "Droid",
"fields": [
{
"name": "id",
"type": {
"name": null,
"kind": "NON_NULL"
}
},
{
"name": "name",
"type": {
"name": null,
"kind": "NON_NULL"
}
},
{
"name": "friends",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "friendsConnection",
"type": {
"name": null,
"kind": "NON_NULL"
}
},
{
"name": "appearsIn",
"type": {
"name": null,
"kind": "NON_NULL"
}
},
{
"name": "primaryFunction",
"type": {
"name": "String",
"kind": "SCALAR"
}
}
]
}
}
}
这些是Droid所规定的字段。
ID没有类型名可能会让人感到奇怪。这是因为它是一种名为NON_NULL的”包装器”类型。通过查询该字段的ofType,可以确定它是ID类型,并且不为null。
同样地,”friends”和”appearsIn”也没有类型名称。这些是因为它们是LIST包装器类型。通过向这些类型查询ofType,可以了解每个列表是什么。
{
__type(name: "Droid") {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
}
}
}
{
"data": {
"__type": {
"name": "Droid",
"fields": [
{
"name": "id",
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "ID",
"kind": "SCALAR"
}
}
},
{
"name": "name",
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "String",
"kind": "SCALAR"
}
}
},
{
"name": "friends",
"type": {
"name": null,
"kind": "LIST",
"ofType": {
"name": "Character",
"kind": "INTERFACE"
}
}
},
{
"name": "friendsConnection",
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "FriendsConnection",
"kind": "OBJECT"
}
}
},
{
"name": "appearsIn",
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": null,
"kind": "LIST"
}
}
},
{
"name": "primaryFunction",
"type": {
"name": "String",
"kind": "SCALAR",
"ofType": null
}
}
]
}
}
}
最后,让我们介绍一下特别有用的反省功能作为工具。这是向系统请求文档。
{
__type(name: "Droid") {
name
description
}
}
{
"data": {
"__type": {
"name": "Droid",
"description": null
}
}
}
使用内省技术,可以参考有关类型系统的文件。还可以创建文档浏览器和丰富的集成开发环境。
公式网站这样解释,但关键的代码示例结果是空的。这可能是因为未定义了描述。根据“GraphQL”规范的“Schema Introspection”,可以通过查询操作的根类型引用模式内省系统,其中包含了两个元字段,都具有名为描述(description)的字符串字段。关于文档编写方式,请阅读“Descriptions”(请参考Apollo GraphQL Docs的“Descriptions(文档字符串)”)。
__schema: __Schema!
__type(name: String!): __Type
我简要介绍了自省系统。通过使用此系统,可以查询枚举类型的值以及某个类型实现了哪些接口。此外,自省系统本身也可以被自省。同时,graphql-js/src/type/introspection.ts是实现符合规范的GraphQL查询自省系统的代码。
GraphQL系列的基础
「GraphQL: 查询和变更」
「GraphQL: 架构和类型」
「GraphQL: 验证」
「GraphQL: 执行」
「GraphQL: 内省」