Edit in GitHubLog an issue

Hooks

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

Some use cases for the hooks include:

  • Authenticating a user before all operations

  • Checking for an authorization token before making a request

Hooks increase processing time. Use them sparingly if processing time is important. Hooks are executed in the order you provide them. However, any blocking hooks execute before non-blocking hooks.

Hook arguments

Hooks are plugins that accept the following arguments:

Copied to your clipboard
"hooks": {
"beforeAll": {
"composer": "<Local or Remote file>",
"blocking": true|false
}
}
  • composer (string) - The local or remote file location of the function you want to execute.

    You must add any local scripts to the mesh's files array. Local vs remote functions describes when to use a local or remote function.

    NOTE: Local composer functions are limited to 30 seconds. If blocking is set to true and the function takes longer than 30 seconds, you will receive a Timeout Error. If you repeatedly encounter this error, consider using a remote composer.

  • blocking (boolean) - (false by default) Determines if the query waits for a successful return message before continuing the query.

    The blocking argument allows you to stop running hooks for a query that does not receive a successful response.

    If blocking is true and the composer returns an error, all future hook executions are canceled.

    If blocking is false and the composer returns an error, the composer will still be invoked.

Types of hooks

beforeAll

The beforeAll hook allows you to insert a function before the query takes place. This is a good place to add an authentication layer or anything else you want to run before your query.

Copied to your clipboard
"plugins": [
{
"hooks": {
"beforeAll": {
"composer": "./hooks.js#checkAuthHeader",
"blocking": true
}
}
}
],

afterAll

The afterAll hook allows you to insert a function after the entire operation resolves, but before the response is returned.

afterAll hooks allow a user to provide a function or an endpoint to invoke after executing the operation. afterAll hooks can be used for logging or triggering events. Each hook can be blocking or non-blocking. Non-blocking hooks will not wait for the completion of the execution.

Copied to your clipboard
interface AfterAllTransformObject {
composer: string;
}

beforeSource

The beforeSource hook allows you to insert a function before querying a specific source. This is useful for adding source-specific authentication, logging, or request modification before making requests to individual GraphQL sources.

Copied to your clipboard
"hooks": {
"beforeSource": {
"source1": [
{
"composer": "<Local or Remote file>",
"blocking": true
},
{
"composer": "<Local or Remote file>",
"blocking": true
}
],
"source2": [
{
"composer": "<Local or Remote file>",
"blocking": false
},
{
"composer": "<Local or Remote file>",
"blocking": false
}
]
}
}
Copied to your clipboard
interface BeforeSourceTransformObject {
[sourceName: string]: Array<{
composer: string;
blocking: boolean;
}>;
}

afterSource

The afterSource hook allows you to insert a function after querying a specific source, but before returning the response. This is useful for logging source responses, transforming data, or triggering events after source operations complete.

The afterSource hook uses source names as keys in the configuration object to specify which source the hook should target.

afterSource hooks additionally support blocking behavior to control whether the response waits for the hook to complete.

Copied to your clipboard
"hooks": {
"afterSource": {
"source1": [
{
"composer": "<Local or Remote file>",
"blocking": true
},
{
"composer": "<Local or Remote file>",
"blocking": true
}
],
"source2": [
{
"composer": "<Local or Remote file>",
"blocking": false
},
{
"composer": "<Local or Remote file>",
"blocking": false
}
]
}
}
Copied to your clipboard
interface AfterSourceTransformObject {
[sourceName: string]: Array<{
composer: string;
blocking: boolean;
}>;
}

Local vs remote functions

local composers are defined within your mesh.json file, whereas remote composers are only referenced within your mesh file. Local and remote composers have different advantages and limitations.

local composers

Use local composers if:

  • The entire operation will take less than 30 seconds.

  • The composer logic is simple and only requires access to the headers, body, and other context objects.

Avoid using local composers if:

  • The entire operation will take more than 30 seconds.

  • The composer needs to make network calls.

  • The composer has complex or nested loops.

  • The function uses restricted constructs, including: alert, debugger, eval, new Function(), process, setInterval, setTimeout, WebAssembly, or window.

Local composers require adding any local scripts to the mesh's files array.

Copied to your clipboard
{
"meshConfig": {
"sources": [
{
"name": "MagentoMonolithApi",
"handler": {
"graphql": {
"endpoint": "https://venia.magento.com/graphql"
}
}
}
],
"plugins": [
{
"hooks": {
"beforeAll": {
"composer": "./hooks.js#checkAuthHeader",
"blocking": true
}
}
}
],
"files": [
{
"path": "./hooks.js",
"content": <FILE CONTENT>
}
]
}
}

Fetching from remote origins

Local composers also support fetching from remote origins using fetch().

The following example could be used as a beforeAll hook that validates an authorization token against a remote authorization endpoint using fetch().

Copied to your clipboard
module.exports = {
validateToken: async ({ context }) => {
const { headers } = context;
const { authorization } = headers;
if (!authorization) {
return {
status: "ERROR",
message: "Authorization header is missing",
};
}
try {
// Validate the token against a remote authorization service
const response = await fetch("https://auth.adobe.com/validate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ token: authorization.replace("Bearer ", "") }),
});
const result = await response.json();
if (!response.ok || !result.valid) {
return {
status: "ERROR",
message: "Invalid authorization token",
};
}
return {
status: "SUCCESS",
message: "Token validated successfully",
data: {
headers: {
"x-user-id": result.userId,
},
},
};
} catch (error) {
return {
status: "ERROR",
message: `Token validation failed: ${error.message}`,
};
}
},
};

remote composers

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

remote composers can use the params, context, and document arguments over the network. However, the 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 composer instead.

Example

Copied to your clipboard
{
"meshConfig": {
"sources": [
{
"name": "MagentoMonolithApi",
"handler": {
"graphql": {
"endpoint": "https://venia.magento.com/graphql"
}
}
}
],
"plugins": [
{
"hooks": {
"beforeAll": {
"composer": "<Remote Composer URL>",
"blocking": true
}
}
}
]
}
}

Creating composers

A composer can be a local function or a remote serverless function. Composer signatures differ depending on the hook used and the location of the function.

beforeAll hooks

beforeAll hooks can receive the following fields as objects during runtime:

  • context - An object containing information about the request.

    For example, context can contain headers, the body of the request, and the request object.

  • document - A GraphQL representation of the query.

If the composer is a remote function, all the arguments are sent in the POST body when calling the function.

Local composer example

This simple composer checks for an authorization header before processing the query.

Copied to your clipboard
module.exports = {
isAuth: ({context}) => {
if (!context.headers.authorization) {
return {
status: "ERROR",
message: "Unauthorized",
};
}
return {
status: "SUCCESS",
message: "Authorized",
};
},
};

This remote composer fetches your authorization token and inserts it into the x-auth-token header.

Copied to your clipboard
function getToken({ authorization = "", body = "", url = "" }) {
return `${authorization} - ${body} - ${url}`;
}
module.exports = {
insertToken: ({ context }) => {
const { headers, request, body } = context;
const { authorization } = headers;
const { url } = request;
const authToken = getToken({ authorization, url, body });
return {
status: "SUCCESS",
message: "Authorized",
data: {
headers: {
"x-auth-token": authToken,
},
},
};
},
};

Remote composer example

The following example remote composer checks for an authorization header.

Copied to your clipboard
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
async function handleRequest(event) {
try {
const body = await event.request.json();
if (!body.context.headers["authorization"]) {
return new Response({
status: "SUCCESS",
message: "Unauthorized"
}, {
status: 401
});
}
return new Response({
status: "SUCCESS",
message: "Authorized"
}, {
status: 200
});
} catch (err) {
return new Response(err, {
status: 500
});
}
}

afterAll hooks

afterAll hook composers accept the following arguments:

  • payload - An object containing the operation result with the following structure:
    • result.data - The resolved data from the operation.
    • result.errors - Any errors from the operation.
    • context - The request context.
    • document - The GraphQL operation document.

afterAll hook composers can be local or remote.

Local hook functions have a 30-second timeout. If a local hook function takes longer than 30 seconds, it will timeout and return an error. Non-blocking hooks will not cause the operation to fail even if they timeout.

Examples

Copied to your clipboard
module.exports = {
metaData: async (payload) => {
const originalData = payload.result?.data || {};
const originalErrors = payload.result?.errors || [];
console.log('AfterAll Hook: Adding simple audit trail');
// Extract dynamic information from the GraphQL request/response
const queriedFields = Object.keys(originalData);
const primaryQuery = queriedFields.length > 0 ? queriedFields[0] : 'unknown';
const queryDocument = payload.document || '';
const operationType = queryDocument.toString().includes('mutation') ? 'mutation' : 'query';
// Calculate response size
const responseSize = JSON.stringify(originalData).length;
// Add comprehensive dynamic audit metadata
const auditData = {
...originalData,
_metaData: {
primaryQuery: primaryQuery,
operationType: operationType,
responseSizeBytes: responseSize,
processedBy: 'local-hook'
}
};
return {
status: 'SUCCESS',
message: `Audit trail added for ${primaryQuery} ${operationType}`,
data: {
result: {
data: auditData,
errors: originalErrors
}
}
};
},
};

beforeSource hooks

beforeSource hook composers accept the following arguments:

  • sourceName - The name of the targeted source.
  • request - The request configuration object.
  • operation - The GraphQL operation definition node.

beforeSource hook composers can be local or remote. You can configure multiple hooks for each source, which execute in the specified order.

Examples

The local composer example adds source-specific headers before making requests to the Adobe Commerce API. The remote composer example validates source-specific authentication before making requests.

Copied to your clipboard
module.exports = {
beforeMagentoRequest: ({ sourceName, request, operation }) => {
// Add Commerce-specific authentication headers
const commerceHeaders = {
"x-magento-store": "default",
"x-magento-customer-token": request.headers?.authorization?.replace("Bearer ", "") || "",
};
return {
status: "SUCCESS",
message: "Commerce headers added",
data: {
headers: commerceHeaders,
},
};
},
};

afterSource hooks

afterSource hook composers accept the following arguments:

  • sourceName - The name of the targeted source.
  • request - The request configuration object.
  • operation - The GraphQL operation definition node.
  • response - The response object from the source.
  • setResponse - Function to modify the response.

afterSource hook composers can be local or remote. Multiple hooks can be configured for each source, and they will be executed in order.

Examples

The local composer example logs source responses and modifies the response after source operations. The remote composer example publishes events after source operations complete.

Copied to your clipboard
module.exports = {
afterMagentoResponse: ({ sourceName, request, operation, response, setResponse }) => {
console.log(`Source ${sourceName} returned response:`, response);
// Modify the response if needed
if (sourceName === "MagentoMonolithApi") {
// Example: Add custom headers to the response
const modifiedResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: {
...Object.fromEntries(response.headers.entries()),
"x-processed-by": "magento-hook",
},
});
setResponse(modifiedResponse);
}
return {
status: "SUCCESS",
message: "Source response processed",
};
},
};

Return signatures

The return signature of a composer is the same for local and remote functions.

Copied to your clipboard
{
status: "ERROR" | "SUCCESS",
message: string,
data?: {
headers?: {
[headerName: string]: string
}
}
}

onFetch hooks

You can use the onFetch plugin to intercept and modify HTTP requests before they are sent to your GraphQL sources.

The onFetch plugin can assist with the following use cases:

  • Authentication: Adding dynamic auth tokens or API keys
  • Conditional Headers: Adding headers based on query content or user context
  • Request Tracking: Adding correlation IDs or request timestamps
  • Request Modification: Transforming request body or parameters
  • Logging: Adding custom logging or metrics

The onFetch plugin can also access your execution parameters, such as: root, args, context, and info.

The following example adds a custom header (x-md5-hash) to the request. This could be used to add a hash of the request body to the request headers for security purposes.

Copied to your clipboard
{
"meshConfig": {
"sources": [
{
"name": "CommerceAPI",
"handler": {
"graphql": {
"endpoint": "https://venia.magento.com/graphql"
}
}
}
],
"plugins": [
{
"onFetch": [
{
"source": "commerceAPI",
"handler": "./handleOnFetch.js"
}
]
}
]
}
}
Was this helpful?
  • Privacy
  • Terms of Use
  • Do not sell or share my personal information
  • AdChoices
Copyright © 2025 Adobe. All rights reserved.