跳至主要内容

节点

一个通过其全局对象标识符(字符串)获取节点的步骤。接受两个参数

  • 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;
},
},
};