How to verify and process a Shopify webhook in Node.js on AWS Lambda with API Gateway?

I am a Shopify store owner. In the admin of my store, in Settings > Notifications > Webhooks, I’ve created a webhook for “order creation” and provided my endpoint URL. Shopify provides a string/token the webhooks will be signed with (presumably for me to use in my code). I am not using webhooks in the context of a Shopify app. This is a simple URL endpoint.

My endpoint URL is a AWS API Gateway for a Lambda function using Node.js. I am currently using a Lambda proxy integration in order to access the entire HTTP request as-is (via a catch-all ANY method), although I’m not sure if this is necessary or working.

I am using the Shopify Node.js library and calling the shopify.webhooks.validate method, which accepts 3 parameters:

  1. rawBody: The raw body of the request received by the app.
  2. rawRequest: The HTTP Request object used by your runtime.
  3. rawResponse: The HTTP Response object used by your runtime. Required for Node.js.

I am assuming this method computes the HMAC digest and compares it to the header value so I do not have to write this code myself.

Questions:

How do I access the raw HTTP Request object and HTTP Response object in this Node.js Lambda environment? In Express it would be req and res. But I am not using Express.

Also, where do I use the webhooks signing string/token Shopify generates for me? Do I use that as the ACCESS_TOKEN? Or do I install the Shopify Headless app and use the private access token under Storefront API?

Node.js code (index.js):

import "@shopify/shopify-api/adapters/node";
import { shopifyApi } from "@shopify/shopify-api";

const SHOP_NAME = 'store_shop_name';
const ACCESS_TOKEN = 'store_access_token';

const shopify = shopifyApi({
    shopName: SHOP_NAME,
    accessToken: ACCESS_TOKEN
});

exports.handler = async (event, context) => {
    const {isValid, topic, domain} = await shopify.webhooks.validate({
        rawBody: ?? // request body as string
        rawRequest: ?? // http request object
        rawResponse: ?? // http response object
    });

    if (!isValid) {
        return {
            statusCode: 400
        };
    }
    
    // process here
}

Any relevant examples would be greatly appreciated.

Accessing Raw HTTP Request and Response Objects in Lambda:

  • Lambda Proxy Integration: Since you’re using a Lambda proxy integration, the event object passed to your handler function already contains the raw HTTP request and response information. You can access it directly:
    JavaScript
const rawRequest = event;
const rawResponse = context.succeed; // Or context.fail for error handling

Raw Body: The raw body of the request is typically found within the event.body property:

JavaScript

const rawBody = event.body;

Using the Webhooks Signing String/Token:

  • HMAC Digest Calculation: The shopify.webhooks.validate method handles the HMAC digest calculation and comparison using the webhook signing string/token. You don’t need to perform this manually.
  • No Access Token Required: For webhook validation, you don’t need to provide an access token. The webhook signing string/token is sufficient to verify the request’s authenticity.

Updated Node.js Code:

JavaScript

import "@shopify/shopify-api/adapters/node";
import { shopifyApi } from "@shopify/shopify-api";

const SHOP_NAME = 'store_shop_name';
const WEBHOOK_SIGNING_STRING = 'your_webhook_signing_string'; // Replace with the actual string

const shopify = shopifyApi({
    shopName: SHOP_NAME
});

exports.handler = async (event, context) => {
    const { isValid, topic, domain } = await shopify.webhooks.validate({
        rawBody: event.body,
        rawRequest: event,
        rawResponse: context.succeed // Or context.fail for error handling
    }, WEBHOOK_SIGNING_STRING);

    if (!isValid) {
        return {
            statusCode: 400
        };
    }

    // Process the webhook event here
    if (topic === 'orders/create') {
        // Handle order creation
    }
};

Additional Considerations:

  • Error Handling: Implement proper error handling mechanisms to catch exceptions and provide informative responses.
  • Webhook Topic: Ensure you’re handling the correct webhook topic (e.g., orders/create ) based on your specific requirements.
  • Security: Consider additional security measures, such as verifying the webhook domain and using HTTPS for communication.

By following these guidelines, you should be able to effectively validate and process Shopify webhooks in your Node.js Lambda function.