钩子插件
Hooks允许您在一个目标节点上引用可堆放的本地或远程函数.
虎克的一些使用案例包括:
- 在所有操作前验证用户
- 在提出请求前检查授权令牌
- 在从源获取前修改请求头
- 从源获取后修改响应头
- 图QL 操作后记录执行结果
注: 虎克根据其复杂性在有持续时间时会增加处理时间. 少用点 如果处理时间很重要.
目录
使用量
本地和远程功能在您的配置中定义 。 钩被配置为接受以下内容的插件 参数 :
配置
{
"hooks": {
"beforeAll": {
"composer": "<Local or Remote file>",
"blocking": true|false
},
"beforeSource": {
"sourceName": [
{
"composer": "<Local or Remote file>",
"blocking": true|false
}
]
},
"afterSource": {
"sourceName": [
{
"composer": "<Local or Remote file>",
"blocking": true|false
}
]
},
"afterAll": {
"composer": "<Local or Remote file>",
"blocking": true|false
}
}
}
- 作曲( 字符串) - 您想要执行的函数的本地或远程文件位置.
- 屏蔽( 布尔) - (默认假)确定查询是否在继续之前等待成功的返回消息.
- 来源Name (字符串) - 对于源钩,取出源到目标的名称(如"用户","产品"等).
注: 钩子被按照配置的顺序执行,被封住的钩子运行在非封住的钩子之前. 错误 从非阻塞钩被忽略.
本地和远程函数必须返回符合 HookResponse 接口.
interface HookResponse {
status: "SUCCESS" | "ERROR"
message: string,
data?: {
headers?: {
[headerName: string]: string
}
}
}
在全钩之前
这个 beforeAll 钩子在任何 GraphQL 操作前执行一次 。 它可以访问请求上下文,并可修改标题.
Hook 函数签名 :
interface BeforeAllHookFunctionPayload {
context: {
request: Request;
params: GraphQLParams;
secrets?: Record<string, string>;
state?: StateApi;
logger?: YogaLogger;
};
document?: unknown;
}
interface BeforeAllHookResponse extends HookResponse {
data?: {
headers?: {
[headerName: string]: string;
};
};
}
示例
module.exports = {
addAuthHeader: async ({ context }) => {
const { headers, secrets } = context;
if (!headers.authorization) {
return {
status: 'ERROR',
message: 'Authorization header required'
};
}
return {
status: 'SUCCESS',
message: 'Authorization validated',
data: {
headers: {
'X-Auth-Validated': 'true'
}
}
};
}
};
来源虎克之前
这个 beforeSource 钩子在从指定来源取取数据之前执行一次 。 它可以访问请求对象,允许您修改请求头,正文或其他请求属性.
Hook 函数签名 :
interface BeforeSourceHookFunctionPayload {
context: {
request: Request;
params: GraphQLParams;
secrets?: Record<string, string>;
state?: StateApi;
logger?: YogaLogger;
};
request: RequestInit; // Can be modified
document?: unknown;
sourceName: string;
}
interface BeforeSourceHookResponse extends HookResponse {
data?: {
request?: RequestInit | {
body?: string | ReadableStream<Uint8Array>;
headers?: Record<string, string>;
method?: string;
url?: string;
};
};
}
示例
module.exports = {
modifyRequest: async ({ context, request, sourceName }) => {
// Add authentication header to request
const modifiedRequest = {
...request,
headers: {
...request.headers,
'Authorization': `Bearer ${context.secrets.API_TOKEN}`
}
};
return {
status: 'SUCCESS',
message: 'Request modified successfully',
data: {
request: modifiedRequest
}
};
}
};
来源钩后
这个 afterSource 钩子在从命名源获取数据后执行一次 。 它可以访问响应对象,允许您修改响应头,正体,或其他响应属性.
Hook 函数签名 :
interface AfterSourceHookFunctionPayload {
context: {
request: Request;
params: GraphQLParams;
secrets?: Record<string, string>;
state?: StateApi;
logger?: YogaLogger;
};
document?: unknown;
sourceName: string;
response?: Response; // Can be modified
}
interface AfterSourceHookResponse extends HookResponse {
data?: {
response?: Response | {
body?: string | ReadableStream<Uint8Array>;
headers?: Record<string, string>;
status?: number;
statusText?: string;
};
};
}
示例
module.exports = {
modifyResponse: async ({ context, response, sourceName }) => {
// Add custom header to response
const modifiedResponse = new Response(response.body, {
...response,
headers: {
...response.headers,
'X-Custom-Header': 'modified-by-hook'
}
});
return {
status: 'SUCCESS',
message: 'Response modified successfully',
data: {
response: modifiedResponse
}
};
}
};
全部钩后
这个 afterAll 钩子在GraphQL执行完成后执行一次,但在最后回复发送给客户端之前. 它可以访问执行结果,允许您修改最终响应或进行清理操作.
Hook 函数签名 :
interface AfterAllHookFunctionPayload {
context: {
request: Request;
params: GraphQLParams;
secrets?: Record<string, string>;
state?: StateApi;
logger?: YogaLogger;
};
document?: unknown;
result?: GraphQLResult; // The final execution result
}
interface AfterAllHookResponse extends HookResponse {
data?: {
result?: GraphQLResult; // Can be modified
};
}
示例
module.exports = {
logExecution: async ({ context, result }) => {
// Log execution result
context.logger.info('GraphQL execution completed', {
hasErrors: result.errors && result.errors.length > 0,
dataKeys: result.data ? Object.keys(result.data) : []
});
return {
status: 'SUCCESS',
message: 'Execution logged successfully'
};
}
};
本地函数
本地函数是JavaScript函数,与服务器捆绑并被执行. 应该写成: 从引用的钩子模块导出常见的JS函数,或者默认,或者命名为导出.
在下列情况下避免使用本地函数:
- 整个行动需要30多秒.
- 该函数使用受限构造,包括:
process, (中文(简体) ),window, (中文(简体) ),debugger, (中文(简体) ),alert, (中文(简体) ),setTimeout, (中文(简体) ),setInterval, (中文(简体) ),new Function(), (中文(简体) ),eval,或WebAssembly。 。 。 .
本地函数的例子如下:
module.exports = {
/**
* Hook function to validate headers against context secret.
* @type {import('@adobe/plugin-hooks').HookFunction} Hook function
* @param {import('@adobe/plugin-hooks').HookFunctionPayload} Hook payload
* @returns {Promise<import('@adobe/plugin-hooks').HookResponse>} Hook response
*/
isAuth: async ({context}) => {
function test() {}
const {
headers,
secrets,
} = context;
test();
if (headers.authorization !== secrets.TOKEN) {
return {
status: 'ERROR',
message: "Unauthorized",
};
} else {
return {
status: "SUCCESS",
message: "Authorized",
};
}
},
}
见 实例 用于补充本地功能的例子.
远程函数
如果某个本地函数不起作用或导致超时出错,考虑使用远程函数.
您可以自由使用任何具有远程功能的语言,框架或库,只要它们返回有效的响应.
远程函数必须使用 HTTPS 协议并可从互联网上访问。 远程请求
函数使用 POST HTTP 方法 。 由于涉及额外的网络跳动,远程功能可以增加耐久性.
远程函数可以使用 params, (中文(简体) ), context,以及 document 在网络上争论。 然而,序列化和
jSON数据的去序列化意味着任何复杂的字段或引用都会被丢失. 如果作曲家依赖
复数字段或引用,考虑取而代之使用本地函数.
移徙指南
中断更改
1. 模块结构
这套方案现在既提供了无害环境管理,也提供了共同的JS产出:
- 无害环境管理数字 :
dist/esm/index.js - 常见JS数字 :
dist/cjs/index.js - 类型脚本声明数字 :
dist/types/index.d.ts
2. 进口变化
在此之前:
const hooksPlugin = require('@adobe/plugin-hooks');
之后:
// CommonJS
const hooksPlugin = require('@adobe/plugin-hooks');
// ESM
import hooksPlugin from '@adobe/plugin-hooks';
3. Hook 函数签名
Hook功能现在接收到适当打出的有效载荷. 基本结构保持不变,但TypeScript用户将获得更好的类型安全.
在此之前:
module.exports = {
myHook: async (payload) => {
// payload structure was loosely defined
}
};
之后:
module.exports = {
myHook: async (payload) => {
// payload is now properly typed
const { context, document } = payload;
const { headers, secrets, state } = context;
}
};
4. 处理错误
现在错误使用 GraphQL 错误代码进行更好的整合 :
在此之前:
return {
status: 'ERROR',
message: 'Unauthorized'
};
之后:
// Errors are automatically wrapped with GraphQL error codes
return {
status: 'ERROR',
message: 'Unauthorized'
};
// Results in GraphQLError with proper error code
新特性
1. 原始钩
您现在可以以 beforeSource 财务报告和审定财务报表 afterSource 钩子 :
{
"hooks": {
"beforeSource": {
"users": [
{
"composer": "./hooks/authHook.js",
"blocking": true
}
]
}
}
}
2. 在"通钩"之后
在 GraphQL 执行后执行代码 :
{
"hooks": {
"afterAll": {
"composer": "./hooks/loggingHook.js",
"blocking": false
}
}
}
3. 国家API
访问钩中的持续状态 :
module.exports = {
myHook: async ({ context }) => {
const { state } = context;
// Store data
await state.put('user-session', 'session-data', { ttl: 3600 });
// Retrieve data
const session = await state.get('user-session');
// Delete data
await state.delete('user-session');
}
};
类型脚本支持
如果您正在使用 TypeScript , 您现在可以导入类型以获取更好的开发经验 :
import type {
HookFunction,
HookFunctionPayload,
HookResponse,
BeforeSourceHookFunctionPayload,
AfterSourceHookFunctionPayload,
AfterAllHookFunctionPayload
} from '@adobe/plugin-hooks';
const myHook: HookFunction = async (payload: HookFunctionPayload) => {
// Fully typed payload
};
测试
一揽子计划现在包括全面的测试范围。 您可以在当地进行测试 :
yarn test
yarn test --coverage
业绩考量
- 源钩 其效率高于全球钩子,因为它们只运行特定源
- 非阻塞钩 不要影响反应时间
- 状态 API 提供持续存储,但 TTL 有限制
- 纪念 为更好的性能自动应用
发展
安装
先决条件
- 节点.js (v18或以后)
林特
运行嵌入脚本检查出错 :
yarn lint
测试
运行测试脚本以执行所有测试 :
yarn test
测试范围包括: --coverage 旗帜 :
yarn test --coverage
构建
运行构建脚本将 TypeScript 代码编译为ESM/CJS :
yarn build
建筑产出将在 dist 目录.
捐款
请参看 提供指南 更多信息.
许可证发放
这个项目根据Apache V2许可证获得许可. 见 自由主义 更多信息.