加入我们,于 10 月 8 日至 10 日在纽约市学习关于 GraphQL Federation 和 API 平台工程的最新技巧、趋势和新闻。加入我们,参加 2024 年纽约市的 GraphQL 峰会
文档
免费开始

创建 Apollo Server 插件

通过自定义功能扩展 Apollo Server


所有插件生命周期方法是async除了 willResolveFieldschemaDidLoadOrUpdate

你可以创建自己的 插件来执行自定义 以响应某些事件。例如,一个基本的日志记录插件可能会记录与 Apollo Server 相关联的 字符串。

插件的结构

插件是JavaScript对象,它们实现一个或多个响应事件的功能。以下是一个基本的插件,用于响应serverWillStart 事件:

index.ts
const myPlugin = {
async serverWillStart() {
console.log('Server starting up!');
},
};
index.js
const myPlugin = {
async serverWillStart() {
console.log('Server starting up!');
},
};

你可以在初始化 contextValue的地方定义插件,或者将其作为单独的模块导出:

myPlugin.ts
export default {
async serverWillStart() {
console.log('Server starting up!');
},
};
myPlugin.js
export default {
async serverWillStart() {
console.log('Server starting up!');
},
};

为了创建一个接受选项的插件,创建一个接受一个options对象并返回一个正确结构的插件对象的函数,如下所示:

myPlugin.ts
export default (options: { logMessage: string }) => {
return {
async serverWillStart() {
console.log(options.logMessage);
},
};
};
myPlugin.js
export default (options) => {
return {
async serverWillStart() {
console.log(options.logMessage);
},
};
};

如果你的插件需要读取请求的contextValue(即requestContext.contextValue),你必须将你的插件声明为ApolloServerPlugin<YourContextType>,如下所示:

myPlugin.ts
interface MyContext {
token: string;
}
export default function (): ApolloServerPlugin<MyContext> {
return {
async requestDidStart({ contextValue }) {
// token is properly inferred as a string
console.log(contextValue.token);
},
};
}
myPlugin.js
export default function () {
return {
async requestDidStart({ contextValue }) {
// token is properly inferred as a string
console.log(contextValue.token);
},
};
}

响应事件

插件通过实现与这些事件对应的功能来精确指定它响应哪些事件。上述示例中的插件响应的是 serverWillStart 事件,当 Apollo Server 准备启动时将触发该事件。几乎所有插件事件都是 async 函数(即返回 Promise 的函数)。唯一的例外是 willResolveFieldschemaDidLoadOrUpdate

插件可以响应任何组合的 受支持事件

响应请求生命周期事件

插件可以响应以下与 GraphQL 请求生命周期相关的事件:

然而,您定义这些函数的方式与上面给出的 serverWillStart 示例略有不同。首先,您的插件必须定义 requestDidStart 函数:

const myPlugin = {
async requestDidStart() {
console.log('Request started!');
},
};
const myPlugin = {
async requestDidStart() {
console.log('Request started!');
},
};

每当 Apollo Server 收到一个 GraphQL 请求时,就会触发 requestDidStart 事件,这在该列表中列出的其他生命周期事件之前。您可以像响应 serverWillStart 事件一样响应此事件,但您还可以使用该函数来定义响应请求生命周期事件的逻辑,如下所示:

const myPlugin = {
async requestDidStart(requestContext) {
console.log('Request started!');
return {
async parsingDidStart(requestContext) {
console.log('Parsing started!');
},
async validationDidStart(requestContext) {
console.log('Validation started!');
},
};
},
};
const myPlugin = {
async requestDidStart(requestContext) {
console.log('Request started!');
return {
async parsingDidStart(requestContext) {
console.log('Parsing started!');
},
async validationDidStart(requestContext) {
console.log('Validation started!');
},
};
},
};

如图所示,requestDidStart 函数可以返回一个对象,该对象定义了响应请求生命周期事件的函数。这种结构可以组织和封装所有插件请求生命周期逻辑,使其更容易理解。

请求生命周期事件流程

以下图表说明了每个请求发生的事件序列。每个事件都在 Apollo Server 插件事件 中有详细的文档。

重要:以下任何可能导致“成功”的事件也可能导致错误。每当发生错误时,都会触发 didEncounterErrors 事件,并且“成功”路径的其余部分都不会执行。

Success
Success
Success
Success
Response provided
No response provided
Success
(Fires once per resolver)
requestDidStart
didResolveSource
parsingDidStart*
validationDidStart*
didResolveOperation
responseForOperation
executionDidStart*
willResolveField*
didEncounterErrors
willSendResponse

*指示的事件也支持 结束钩子,当关联步骤 完成 时会调用它们。

结束钩子

以下事件的处理程序可以选择返回一个在相关生命周期阶段 结束时 调用的函数:

executionDidStart 钩子一样,executionDidStart 钩子返回一个包含 executionDidEnd 函数的对象,而不是仅作为结束钩子返回一个函数。这是因为返回的对象还可以包含 willResolveField

与其他事件处理程序一样,这些结束钩子都是异步函数(除了 willResolveField 的结束钩子)。

生命周期阶段执行过程中发生的任何错误都会通过端钩传递。例如,以下插件会记录上述任何生命周期事件中发生的任何错误。

const myPlugin = {
async requestDidStart() {
return {
async parsingDidStart() {
return async (err) => {
if (err) {
console.error(err);
}
};
},
async validationDidStart() {
// This end hook is unique in that it can receive an array of errors,
// which will contain every validation error that occurred.
return async (errs) => {
if (errs) {
errs.forEach((err) => console.error(err));
}
};
},
async executionDidStart() {
return {
async executionDidEnd(err) {
if (err) {
console.error(err);
}
},
};
},
};
},
};
const myPlugin = {
async requestDidStart() {
return {
async parsingDidStart() {
return async (err) => {
if (err) {
console.error(err);
}
};
},
async validationDidStart() {
// This end hook is unique in that it can receive an array of errors,
// which will contain every validation error that occurred.
return async (errs) => {
if (errs) {
errs.forEach((err) => console.error(err));
}
};
},
async executionDidStart() {
return {
async executionDidEnd(err) {
if (err) {
console.error(err);
}
},
};
},
};
},
};

请注意,validationDidStart端钩会收到一个包含发生过的所有验证错误的数组(如果有)。willResolveField端钩收到抛出的错误作为第一个,并将解析器的结果作为第二个参数。每个端钩的参数在请求生命周期事件中的类型定义中有说明。

检查请求和响应详情

如上例所示,requestDidStart和请求生命周期函数接收一个requestContext参数。此参数类型为GraphQLRequestContext,它包括一个request(类型为GraphQLRequest),还有可用的response(类型为GraphQLResponse)。

这些类型及其相关子类型都在@apollo/server中定义。

安装自定义插件

通过向ApolloServer构造函数提供一个plugins配置选项,将您的插件添加到Apollo Server,如下所示:

import { ApolloServer } from '@apollo/server';
import ApolloServerOperationRegistry from '@apollo/server-plugin-operation-registry';
/* This example doesn't provide `typeDefs` or `resolvers`,
both of which are required to start the server. */
import { typeDefs, resolvers } from './separatelyDefined';
const server = new ApolloServer({
typeDefs,
resolvers,
// You can import plugins or define them in-line, as shown:
plugins: [
/* This plugin is from a package that's imported above. */
ApolloServerOperationRegistry({
/* options */
}),
/* This plugin is imported in-place. */
require('./localPluginModule'),
/* This plugin is defined in-line. */
{
async serverWillStart() {
console.log('Server starting up!');
},
},
],
});
import { ApolloServer } from '@apollo/server';
import ApolloServerOperationRegistry from '@apollo/server-plugin-operation-registry';
/* This example doesn't provide `typeDefs` or `resolvers`,
both of which are required to start the server. */
import { typeDefs, resolvers } from './separatelyDefined';
const server = new ApolloServer({
typeDefs,
resolvers,
// You can import plugins or define them in-line, as shown:
plugins: [
/* This plugin is from a package that's imported above. */
ApolloServerOperationRegistry({
/* options */
}),
/* This plugin is imported in-place. */
require('./localPluginModule'),
/* This plugin is defined in-line. */
{
async serverWillStart() {
console.log('Server starting up!');
},
},
],
});
上一页
联合订阅
下一页
事件参考
评价文章评价在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,商业名称 Apollo GraphQL。

隐私政策

公司