入门
安装
Grafast 是 GraphQL 的替代执行层;我们仍然需要 GraphQL.js 来构建模式,解析和验证请求。因此,您入门的第一件事是安装 grafast
和 graphql
- npm
- Yarn
- pnpm
npm install --save grafast@beta graphql
yarn add grafast@beta graphql
pnpm add grafast@beta graphql
我们打算编写一个规范,以便其他语言可以实现 Grafast 执行策略,但目前 Grafast 仅限于 JavaScript/TypeScript。
如果您已有现有的 GraphQL.js schema,您可以使用 Grafast 运行它 - 请参阅 使用现有 schema。
TypeScript v5.0.0+(可选)
我们建议您使用 TypeScript 以获得最佳体验 - 自动完成、内联文档等。
您无需使用 TypeScript 即可使用 Grafast,但如果您使用,则必须使用 TypeScript v5.0.0 及更高版本,并将其配置为支持 package.json
中的 exports
属性,您可以在 TypeScript 配置中添加以下内容
"moduleResolution": "node16", // Or "nodenext"
我们对 semver 的遵守 **不涵盖类型** - 我们可能会在补丁级别更新中对 TypeScript 类型进行重大更改。这样做的原因是 TypeScript 本身一直在不断变化,我们依赖的库经常进行重大类型更改,迫使我们也这样做。此外,对类型的改进通常对开发者体验来说是一件好事,即使这意味着您可能需要在更新后花几分钟时间来解决任何问题。
但是,我们尽量使 TypeScript 类型尽可能稳定,只在它们的益处超过成本时进行重大更改(由我们的维护者决定),并且我们尽力在发行说明中详细说明如何处理这些更改(如果需要采取任何措施)。
我的第一个计划
让我们构建一个由 Grafast 计划支持的简单 GraphQL schema 并查询它。
构建 GraphQL schema 的方法有很多,在本例中我们将使用“schema first”方法,但 Grafast schema 可以使用“code first”或“database first”或任何其他方法生成。
我们有一个 playground,你可以用来实验 Grafast,而无需安装任何软件。
首先,让我们定义我们的 GraphQL schema。我们将使用一个非常简单的 schema,它只有一个字段,将它的两个参数加在一起。
const typeDefs = /* GraphQL */ `
type Query {
addTwoNumbers(a: Int!, b: Int!): Int
}
`;
现在我们需要为 schema 定义 plans
。我们 Query.addTwoNumbers
字段的计划是 读取参数,然后使用 lambda 步骤将它们加在一起。lambda 步骤接受一个其他步骤的列表,然后通过对每组结果值调用给定的回调来确定结果。
const { lambda } = require("grafast");
const plans = {
Query: {
addTwoNumbers(_, fieldArgs) {
const $a = fieldArgs.get("a");
const $b = fieldArgs.get("b");
return lambda([$a, $b], ([a, b]) => a + b);
},
},
};
lambda
有点像一个逃生舱口——它允许对值进行逐个处理,而不是 Grafast 为了效率而偏好的批量处理。当批量处理不会带来任何好处时,它可以作为实用函数使用,但总的来说,你应该选择一个 更合适的步骤。
将 lambda
的回调函数设置为全局(定义一次)函数将使 Grafast 能够潜在地检测到它的多次使用并对其进行重复数据删除。如果一个类似的 lambda 回调在查询中的许多地方使用,这对性能很重要。
为了将它转换为一个 schema,我们可以使用 makeGrafastSchema
助手,它将 typeDefs
和 plans
拼接在一起。
const { makeGrafastSchema } = require("grafast");
const schema = makeGrafastSchema({
typeDefs,
plans,
});
最后,我们可以运行我们的查询。
const { grafastSync } = require("grafast");
const result = grafastSync({
schema,
source: /* GraphQL */ `
{
addTwoNumbers(a: 40, b: 2)
}
`,
});
结果是我们预期的。
{
"data": {
"addTwoNumbers": 42
}
}
我们的 schema 非常简单,查询可以与 grafastSync(...)
同步运行,但现实世界中的大多数查询应该通过 grafast(...)
执行,并将返回结果、对结果的承诺,甚至对于 @stream
、@defer
、订阅和类似内容的异步可迭代对象!
然后我们可以 通过 HTTP 提供此 schema,使用像 grafserv 这样的服务器或任何 支持 envelop 的服务器。
我的第一个步骤类
操作计划的构建块是“步骤”。步骤是“步骤类”的实例,Grafast 提供了一系列有限的 标准步骤 供你使用;但当这些步骤不够用时,你可以编写自己的步骤。步骤类扩展了 ExecutableStep
类。唯一需要定义的方法是 execute
,但是大多数步骤还将有一个 constructor
,在其中它们接受它们的 arguments(其中一些可能是依赖项),并且还可能具有各种生命周期方法。
关于如何执行此操作的完整细节可以在 步骤类 中找到,但让我们现在构建一个简单的步骤类来替换上面的 lambda
使用方法。
const { ExecutableStep } = require("grafast");
class AddStep extends ExecutableStep {
constructor($a, $b) {
super();
this.addDependency($a);
this.addDependency($b);
}
execute({ indexMap, values: [aDep, bDep] }) {
return indexMap((i) => {
const a = aDep.at(i);
const b = bDep.at(i);
return a + b;
});
}
}
按照惯例,我们始终定义一个函数来构造我们类的实例。
function add($a, $b) {
return new AddStep($a, $b);
}
这样做有很多原因,一个简单的原因是使计划代码更容易阅读:我们不会在计划解析器函数中看到new
调用,也不会看到冗余的Step
措辞,从而提高信噪比。更重要的是,这层间接层允许我们在传递给类构造函数之前进行一些小的操作,并使 API 更具未来可扩展性,因为我们可以在将来让函数返回不同的内容,而无需重构架构中的计划。请记住,这种成本只在计划时产生(通常是缓存的,并且可以重复用于类似的未来请求),并且每个字段只计划一次,因此额外函数调用的开销可以忽略不计。
现在我们可以使用此函数来添加我们的数字,而不是 lambda 计划
const plans = {
Query: {
addTwoNumbers(_, args) {
const $a = args.get("a");
const $b = args.get("b");
- return lambda([$a, $b], ([a, b]) => a + b);
+ return add($a, $b);
},
},
};
你可能能够使用现成的步骤类编写整个 Grafast 架构,但了解步骤类的工作原理非常重要,以防你想进一步优化。 阅读有关步骤类的更多信息,或继续浏览文档。