GraphQL —— 标量类型
标量(ScalarTypeDefinition)是 GraphQL 中不可分割的原子数据类型,在服务中充当叶子节点。对于客户端而言,合法的查询集(Select Set)必须到达叶子节点,也就是到达标量类型节点。
GraphQL 规范提供了五种标量:
- Int: 32 位有符号整型,超出精度范围后,引擎会抛出异常
- Float: 有符号双精度浮点数,超出精度范围后,引擎会抛出异常
- String: 字符串,用于表示
UTF-8
字符序列 - Boolean: bool 值
- ID: 资源唯一标志符
1. ID 特性
上述五种类型与其他语言对应的类型定义相似,相信读者老爷们都已经非常熟悉,无需赘述,唯一值得探讨的是 ID
类型。
- 表现上
ID
类型只是一个字符串格式的值,引擎支持字符串解析值,也支持将Int
解析值转换为字符串类型; - 语义上"
ID
" 类型应该用于唯一标志一个资源对象,也就是说,使用相同 ID 值,无论查询多少次,结果都应该是同一对象,这一点有助于实现缓存,是 GraphQL 推荐的缓存方案; - 引擎并不限制解析值的唯一性,查询结果包含多个 ID 值相同的节点是合法的。
我们来看一下例子加深印象:
[ // 字符串类型 {id: '1'}, // int 类型,引擎会将其转换为字符串 {id: 1}, // float 类型 // 非法值,引擎不支持float转换 // 将抛出 `TypeError` 错误 {id: 1.2}, // 与上面第一条重复 // 合法值,引擎并不强制 `ID` 值的唯一性 {id: '1'} ]
2. 自定义标量类型
除规范定义的标量外,还可以按需定义业务范畴内的标量。语法非常简单:
scalar Datetime
注意,这只是语义范畴定义,还需要定义序列化、反序列化函数:
new GraphQLScalarType({ name: "Datetime", description: "日期时间标量类型", // 序列化函数 serialize(value) { return value.toString(); }, // 解析函数 parseValue(value) { if (typeof value === "string") { return new Date(value); } throw new Error("参数类型错误"); }, // 解析函数 parseLiteral(ast) { if (ast.kind === Kind.STRING) { return new Date(ast.value); } throw new Error("参数类型错误"); } });
下面我们一个一个看这些配置:
name
: 字段名,请保持与 schema 中定的标量类型名称保持一致description
: 类型描述,在一些诊断工具上还是很有用的serialize
: 序列化函数,用于将结果转换为适合 http 传输的数值类型parseValue
: 解析函数,用于将客户端通过 variables 参数传递的数值为 Date 类型parseLiteral
: 同样是解析函数,将客户端传递的 字面量参数 解析为 Date 类型
配置中的 parseValue
、parseLiteral
两个函数功能上相似,都用于解析客户端参数,分别处理两种参数传递方式:
# variables 参数 # 引擎将调用 parseValue 函数 query (before: Datetime){ users(before: $before) { id name } } variables { before: "1991-02-19" } # 字面量参数 # 引擎将调用 parseLiteral 函数 query { users(before: "1991-02-19") { id name } }
最后说一些注意的点:
- 如果类型确定不会作为
InputType
,可以省略parseValue
、parseLiteral
。 parseValue
接收到的是variables
对象中对应的值;而parseLiteral
接收的则是引擎从query
语句中解析出的 AST 节点。AST 节点内容形如:
{ // 字面量类型 "kind": "StringValue", // 字面量值 "value": "1991-02-19", // 指明字面量是否为 [BlockStringValue](https://facebook.github.io/graphql/June2018/#BlockStringValue()) 类型 "block": false, // token 位置 "loc": { "start": 18, "end": 30 } }
3. 返回对象的标量
标量类型也支持返回结构化的对象,只要能为引擎提供符合规则的 serialize
函数,一切皆有可能。我们可以写出这样一个标量:
// Address 对象类型,不过这是一个标量 new GraphQLScalarType({ name: "Address", description: "对象类型的标量", serialize(value) { // value 为对象类型 // value = { city: '深圳', province: '广东省', country: '中国' } return value; } });
但是要注意,标量类型是 不可分割 的,不能再传入查询子集:
# 合法请求 query { users { id name # Address 类型值 bornOrigin } }
返回结果:
{ "data": { "users": [ { "id": "1", "name": "foo", "bornOrigin": { "city": "深圳", "province": "广东省", "country": "中国" } } ] } }
完整代码在 此处。
能做是一回事,该不该这么做,又是另一回事,返回对象的标量虽然合乎规则,但却违反了 按需加载 这一重要原则,没有实际意义也不值得推崇。
总结
标量是 GraphQL 中的原子类型,一般充当查询的叶子节点。
GraphQL 规范提供了五种标量类型,其中 ID
最为特殊,用于唯一标志一个资源实例。
在标准标量之外,也可以按需定义新的标量,规则如上。
相关推荐
sichenglain 2020-10-27
zhyue 2020-09-28
0linker 2020-09-01
sichenglain 2020-05-19
FZEROF 2020-04-26
zehuawong 2020-04-07
zehuawong 2020-02-11
acloudhuang 2020-01-18
IT新技术 2020-01-07
wikowin 2019-12-15
FZEROF 2019-12-09
sichenglain 2019-11-21
chzh0 2019-11-19
FZEROF 2019-11-19
月光恋九霄 2019-11-18
zehuawong 2019-11-08
HelloWood 2018-09-08
zehuawong 2018-09-08
0linker 2019-10-31