节点
一个通过其全局对象标识符(字符串)获取节点的步骤。接受两个参数
handlers
:从 typeName 到处理程序规范的映射(要使用的编解码器,如何查找记录等) - 参见下文$id
:表示节点 ID 的步骤(通常从字段参数提供)
返回一个多态能力步骤,表示此$id
表示的记录。
用法
const $nodeIdString = fieldArgs.get("id");
const $node = node(handlers, $nodeIdString);
编解码器
节点标识符是一个字符串,在 GraphQL 架构中唯一标识一个实体,在该实体的生命周期内保持不变。
编解码器负责解析和反解析此字符串。节点标识符的编码方式有很多种,因此我们允许使用多种不同的编解码器。
一个编解码器由一个 name
(字符串)和两个方法组成
encode
接受一个中间表示,并将其转换为最终的节点标识符字符串decode
接受最终的节点标识符字符串,并将其转换回中间表示
此中间表示由处理程序生成并由处理程序使用(见下文)。
以下是一个 base64JSONCodec
的示例,它只是将中间表示 JSON 字符串化,然后对其进行 base64 编码
function base64JSONEncode(value: any): string | null {
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
}
base64JSONEncode.isSyncAndSafe = true; // Optimization
function base64JSONDecode(value: string): any {
return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
}
base64JSONDecode.isSyncAndSafe = true; // Optimization
const base64JSONCodec = {
name: "base64JSON",
encode: base64JSONEncode,
decode: base64JSONDecode,
};
处理程序
每个支持 Node
接口的 GraphQL 对象类型都必须有自己的 NodeIdHandler
。此处理程序指定
typeName
- 处理程序适用的 GraphQL 对象类型名称codec
- 与此处理程序一起使用的 NodeID 编解码器(见上文)match
- 确定给定的节点标识符字符串的中间表示(即codec.decode()
的结果)是否与该类型相关plan
- 接受给定对象类型的实体,并返回一个表示此实体的中间表示的步骤(准备馈送到codec.encode()
)getSpec
- 从表示匹配中间表示的步骤构建实体的“规范”get
- 给定来自上述getSpec
的规范,返回一个表示由匹配的节点标识符字符串标识的实体的步骤,如果存在的话
规范(从 getSpec
返回)可能因对象类型而异。它们可以是简单的步骤,例如仅表示数据库中的数字主键,或者它们可以更复杂,例如具有多个键的对象,其中每个键的值都是一个步骤,表示要匹配的远程源中的相关值。
这是一个 userHandler
的示例,它可以在处理 User
类型节点标识符时跨架构使用。
const USER = "User";
const userHandler = {
typeName: USER,
codec: base64JSONCodec,
// Given a User record, return a step describing the data to be encoded by
// the codec:
plan($user: PgSelectSingleStep) {
return list([constant(USER), $user.get("id")]);
},
// Given the data decoded by the codec, determine if the data is for our
// type. In this particular handler, the check looks at the first entry in
// the list to see if it matches our type name.
match(list) {
return list[0] === USER;
},
// Given a step representing decoded data that passes the `match` test above,
// return a specifier object that can be used to retrieve or reference
// this entity.
getSpec($list: ListStep<any[]>) {
return {
id: access($list, 1),
};
},
// Given a spec (the result of `getSpec` above), return a step that resolves
// to the entity (if found).
get(spec: any) {
return pgResource.get(spec);
},
};
const handlers = {
User: userHandler,
// Add more handlers here
};
specFromNodeId
假设您有一个由步骤 $id
表示的节点 ID,并且您已经知道它应该是什么类型(例如,对于 updateUser
变异,您可能知道 $id
应该代表一个 User
),您可以使用 specFromNodeId
传递相关的处理程序来获取所述实体的规范。这在您想要变异实体而无需实际检索它时通常很有用(如果您想检索它,则可以使用上面的 node()
代替)。
如果处理程序不匹配,则结果规范中的可执行步骤将解析为 null 值(或者可能引发错误)。
function specFromNodeId(
handler: NodeIdHandler<any>,
$id: ExecutableStep<string> | AnyInputStep,
): any;
这是一个使用上面 userHandler
示例处理程序的 updateUser
变异的示例。
const typeDefs = /* GraphQL */ `
extend type Mutation {
updateUser(id: ID!, patch: UserPatch!): UpdateUserPayload
}
`;
const plans = {
Mutation: {
updateUser(parent, { $id }) {
// Turn the $id into a specifier:
const spec = specFromNodeId(userHandler, $id);
// Now use this specifier to plan an update for this user:
const $result = pgUpdateSingle(userSource, spec);
// Leave space in our result so we can add more properties later:
const $payload = object({ result: $result });
// Apply all the plans from the 'patch' argument (omitted for brevity):
fieldArgs.apply($payload);
// Return the payload plan:
return $payload;
},
},
};