@adobe/plugin-hooks

中文文档
0.4.0发布于 2025/08/06Apache-2.0TypeScriptESM + CJS近 30 天下载 1991

Adobe API Mesh Hooks Plugin

原始使用文档
文档目录22 节
  1. Table of Contents
  2. Usage
  3. Configuration
  4. BeforeAll Hook
  5. BeforeSource Hook
  6. AfterSource Hook
  7. AfterAll Hook
  8. Local Functions
  9. Remote Functions
  10. Migration Guide
  11. Breaking Changes
  12. New Features
  13. TypeScript Support
  14. Testing
  15. Performance Considerations
  16. Development
  17. Installation
  18. Lint
  19. Test
  20. Build
  21. Contributing
  22. Licensing

Hooks Plugin

Hooks allow you to invoke composable local or remote functions on a targeted node.

Some use cases for Hooks include:

  • Authenticating a user before all operations
  • Checking for an authorization token before making a request
  • Modifying request headers before fetching from a source
  • Modifying response headers after fetching from a source
  • Logging execution results after GraphQL operations

NOTE: Hooks increase processing time when blocking with duration based on their complexity. Use them sparingly if processing time is important.

Table of Contents

Usage

Local and remote functions are defined in your configuration. Hooks are configured as plugins that accept the following arguments:

Configuration

{
  "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
    }
  }
}
  • composer (string) - The local or remote file location of the function you want to execute.
  • blocking (boolean) - (false by default) Determines whether the query waits for a successful return message before continuing.
  • sourceName (string) - For source hooks, the name of the source to target (e.g., "users", "products").

NOTE: Hooks are executed in the order configured, with blocking hooks running before non-blocking ones. Errors from non-blocking hooks are ignored.

Local and remote functions must return an object that conforms to the HookResponse interface.

interface HookResponse {
  status: "SUCCESS" | "ERROR"
  message: string,
  data?: {
    headers?: {
        [headerName: string]: string
    }
  }
}

BeforeAll Hook

The beforeAll hook executes once before any GraphQL operation. It has access to the request context and can modify headers.

Hook Function Signature:

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

Example:

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

The beforeSource hook executes once before fetching data from a named source. It has access to the request object, allowing you to modify request headers, body, or other request properties.

Hook Function Signature:

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

Example:

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

The afterSource hook executes once after fetching data from a named source. It has access to the response object, allowing you to modify response headers, body, or other response properties.

Hook Function Signature:

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

Example:

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 Hook

The afterAll hook executes once after GraphQL execution is complete, but before the final response is sent to the client. It has access to the execution result, allowing you to modify the final response or perform cleanup operations.

Hook Function Signature:

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

Example:

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'
    };
  }
};

Local Functions

Local functions are JavaScript functions that are bundled with and executed on the server. They should be written as CommonJS functions exported from the referenced hooks module, either a default or named export.

Avoid using local functions if:

  • The entire operation will take more than 30 seconds.
  • The function uses restricted constructs, including process, window, debugger, alert, setTimeout, setInterval, new Function(), eval, or WebAssembly.

An example of a local function is shown below:

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

See examples for additional examples of local functions.

Remote Functions

If a local function does not work or causes timeout errors, consider using a remote function.

You are free to use any language, framework, or library with remote functions, as long as they return a valid response. A remote function must be served with the HTTPS protocol and be accessible from the internet. Requests to remote functions use the POST HTTP method. Remote functions can increase latency due to the additional network hop involved.

Remote functions can use the params, context, and document arguments over the network. However, serialization and deserialization of JSON data means that any complex fields or references will be lost. If the composer depends on complex fields or references, consider using a local function instead.

Migration Guide

Breaking Changes

1. Module Structure

The package now provides both ESM and CommonJS outputs:

  • ESM: dist/esm/index.js
  • CommonJS: dist/cjs/index.js
  • TypeScript declarations: dist/types/index.d.ts

2. Import Changes

Before:

const hooksPlugin = require('@adobe/plugin-hooks');

After:

// CommonJS
const hooksPlugin = require('@adobe/plugin-hooks');

// ESM
import hooksPlugin from '@adobe/plugin-hooks';

3. Hook Function Signatures

Hook functions now receive properly typed payloads. The basic structure remains the same, but TypeScript users will get better type safety.

Before:

module.exports = {
  myHook: async (payload) => {
    // payload structure was loosely defined
  }
};

After:

module.exports = {
  myHook: async (payload) => {
    // payload is now properly typed
    const { context, document } = payload;
    const { headers, secrets, state } = context;
  }
};

4. Error Handling

Errors now use GraphQL error codes for better integration:

Before:

return {
  status: 'ERROR',
  message: 'Unauthorized'
};

After:

// Errors are automatically wrapped with GraphQL error codes
return {
  status: 'ERROR',
  message: 'Unauthorized'
};
// Results in GraphQLError with proper error code

New Features

1. Source Hooks

You can now target specific sources with beforeSource and afterSource hooks:

{
  "hooks": {
    "beforeSource": {
      "users": [
        {
          "composer": "./hooks/authHook.js",
          "blocking": true
        }
      ]
    }
  }
}

2. AfterAll Hooks

Execute code after GraphQL execution:

{
  "hooks": {
    "afterAll": {
      "composer": "./hooks/loggingHook.js",
      "blocking": false
    }
  }
}

3. State API

Access persistent state in your hooks:

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 Support

If you're using TypeScript, you can now import types for better development experience:

import type { 
  HookFunction, 
  HookFunctionPayload, 
  HookResponse,
  BeforeSourceHookFunctionPayload,
  AfterSourceHookFunctionPayload,
  AfterAllHookFunctionPayload
} from '@adobe/plugin-hooks';

const myHook: HookFunction = async (payload: HookFunctionPayload) => {
  // Fully typed payload
};

Testing

The package now includes comprehensive test coverage. You can run tests locally:

yarn test
yarn test --coverage

Performance Considerations

  • Source hooks are more efficient than global hooks as they only run for specific sources
  • Non-blocking hooks don't affect response time
  • State API provides persistent storage but has TTL limitations
  • Memoization is automatically applied for better performance

Development

Installation

Prerequisites

Lint

Run the linting script to check for errors:

yarn lint

Test

Run the test script to execute all tests:

yarn test

For test coverage include the --coverage flag:

yarn test --coverage

Build

Run the build script to compile the TypeScript code into ESM/CJS:

yarn build

The build output will be in the dist directory.

Contributing

Please refer to the contributing guidelines for more information.

Licensing

This project is licensed under the Apache V2 License. See LICENSE for more information.

版本与发布记录

@adobe/plugin-hooks 共发布 18 个版本,首次发布于 2023/09/07;过去 12 个月发布了 0 个版本。

版本发布日期距今
0.4.0latest2025/08/061.3 年前
0.3.5-alpha.5alpha2025/08/041.3 年前
0.3.5-alpha.42025/07/241.3 年前
0.3.5-alpha.32025/07/231.3 年前
0.3.5-alpha.22025/07/181.3 年前
0.3.5-alpha.12025/07/151.3 年前

其他发布通道:beta → 0.3.3-beta.1,alpha → 0.3.5-alpha.5。

关于 @adobe/plugin-hooks 的常见问题

@adobe/plugin-hooks 是什么?

Adobe API Mesh Hooks Plugin

如何安装 @adobe/plugin-hooks?

在项目目录运行 npm install @adobe/plugin-hooks,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:graphql(*)、@envelop/core(*)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@adobe/plugin-hooks 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@adobe/plugin-hooks 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@adobe/plugin-hooks 可以商用吗?

@adobe/plugin-hooks 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@adobe/plugin-hooks 的下载量有多少?

根据 npm 官方下载统计,@adobe/plugin-hooks 在 2026/10/05 – 2026/11/03 这 30 天内被下载 1,991 次,日均约 66 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adobe/plugin-hooks 还在维护吗?

最新版本 0.4.0 发布于 2025/08/06(1.3 年前);至今共发布 18 个版本,其中过去 12 个月发布了 0 个;首次发布于 2023/09/07。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。