Build a Simple Shopify Webhook App with Node.js


If you’re building a Shopify app, webhooks are one of the most important tools you’ll need. They help you react to store events like new orders, customer sign-ups, or product updates in real time.

In this post, I’ll show you how to build a simple webhook app using Node.js that listens to the orders/create webhook and verifies its authenticity.


🧰 What You’ll Need

Before we begin, make sure you have:

  • Node.js installed
  • A Shopify Partner account
  • A development store
  • Ngrok (or Cloudflare Tunnel) for local testing
  • Your API Key, Secret, and Store Access Token

📁 Project Setup

Let’s start by creating a new folder and setting up the basic file structure.

shopify-webhook-app/
├── .env
├── index.js
├── verifyWebhook.js
├── package.json

🛠 Step 1: Create package.json

Run this command in your terminal:

npm init -y

Then install the required dependencies:

npm install express dotenv axios crypto body-parser

And update your package.json to:

{
  "name": "shopify-webhook-app",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "axios": "^1.6.0",
    "crypto": "^1.0.1",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "body-parser": "^1.20.2"
  }
}

🔐 Step 2: Set Your Environment Variables

Create a .env file in the root:

SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret
SHOP=mydevstore.myshopify.com
ACCESS_TOKEN=your_access_token
WEBHOOK_TOPIC=orders/create
WEBHOOK_PATH=/webhook/orders-create
APP_URL=https://your-ngrok-url

✅ Step 3: Add Webhook Verification

Create a file called verifyWebhook.js:

import crypto from 'crypto';

export function verifyWebhook(req, res, buf) {
  const hmacHeader = req.get('X-Shopify-Hmac-Sha256');
  const hmac = crypto
    .createHmac('sha256', process.env.SHOPIFY_API_SECRET)
    .update(buf, 'utf8')
    .digest('base64');

  if (hmac !== hmacHeader) {
    throw new Error('HMAC validation failed');
  }
}

🚀 Step 4: Handle and Register the Webhook in index.js

Now let’s create the main app in index.js:

import express from 'express';
import dotenv from 'dotenv';
import axios from 'axios';
import { verifyWebhook } from './verifyWebhook.js';
import bodyParser from 'body-parser';

dotenv.config();

const app = express();
const PORT = 3000;

app.use(
  process.env.WEBHOOK_PATH,
  bodyParser.raw({ type: 'application/json' }),
  (req, res, next) => {
    try {
      verifyWebhook(req, res, req.body);
      next();
    } catch (err) {
      res.status(401).send('Unauthorized');
    }
  }
);

app.post(process.env.WEBHOOK_PATH, (req, res) => {
  const data = JSON.parse(req.body.toString('utf8'));
  console.log('✅ Webhook received:', data);
  res.status(200).send('OK');
});

app.listen(PORT, async () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
  await registerWebhook();
});

async function registerWebhook() {
  try {
    const webhookUrl = `${process.env.APP_URL}${process.env.WEBHOOK_PATH}`;
    const response = await axios.post(
      `https://${process.env.SHOP}/admin/api/2024-04/webhooks.json`,
      {
        webhook: {
          topic: process.env.WEBHOOK_TOPIC,
          address: webhookUrl,
          format: 'json',
        },
      },
      {
        headers: {
          'X-Shopify-Access-Token': process.env.ACCESS_TOKEN,
          'Content-Type': 'application/json',
        },
      }
    );

    console.log('📦 Webhook registered:', response.data.webhook);
  } catch (err) {
    console.error('❌ Error registering webhook:', err.response?.data || err.message);
  }
}

🔍 Testing the Webhook

  1. Run your app:
npm start
  1. Start ngrok:
ngrok http 3000
  1. Copy the HTTPS ngrok URL and update APP_URL in your .env
  2. Go to your development store and place a test order.
  3. Watch your terminal log the webhook data!

🧩 Next Steps

Now that you’ve set up a basic webhook listener, you can:

  • Register multiple webhook topics (like products/update)
  • Add a database (e.g., MongoDB) to log events
  • Turn this into a full embedded Shopify app
  • Host on Railway, Render, or Fly.io

Conclusion

Webhooks are essential for real-time Shopify app experiences. This simple app gives you a strong starting point to build more advanced Shopify integrations.

If you’d like the same version using TypeScript or want to make it an embedded app with Shopify App Bridge, reach out or leave a comment!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.