Hooks
The hooks feature is currently in development and will be expanded in future releases. Only beforeAll hooks are currently available.
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
You cannot use hooks to modify the request or the response. If you want to manipulate data, we recommend that you use custom resolvers.
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
filesarray. Local vs remote functions describes when to use a local or remote function.NOTE: Local composer functions are limited to 30 seconds. If
blockingis set totrueand the function takes longer than 30 seconds, you will receive aTimeout Error. If you repeatedly encounter this error, consider using a remote composer.blocking(boolean) - (falseby default) Determines if the query waits for a successful return message before continuing the query.The
blockingargument allows you to stop running hooks for a query that does not receive a successful response.If blocking is
trueand the composer returns an error, all future hook executions are canceled.If blocking is
falseand 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.
The beforeAll hook is a singular hook.
Copied to your clipboard"plugins": [{"hooks": {"beforeAll": {"composer": "./hooks.js#checkAuthHeader","blocking": true}}}],
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 composer uses restricted constructs, such as
setTimeout,setInterval,for,while,console,process,global, orthrow.
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>}]}}
remote composers
If a local composer does not work or causes timeout errors, consider using a remote composer.
When using remote composers, you could see decreased performance, because remote composers add a network hop.
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,
contextcan containheaders, thebodyof the request, and the requestobject.document- A GraphQL representation of the query.
Since the beforeAll hook runs at the root level, the document object is empty ({}) by default.
If the composer is a remote function, all the arguments are sent in the POST body when calling the function.
Due to the limitations of JSON serialization and de-serialization, some complex JSON fields inside a remote function's arguments might not function correctly over the HTTPS call.
Local composer example
This simple composer checks for an authorization header before processing the query.
Copied to your clipboardmodule.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 clipboardfunction 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.
While this example uses Fastly Edge computing, you can use any serverless function with remote hooks.
Copied to your clipboardaddEventListener("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});}}
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}}}