The Definitive Guide to Handling Shopify Webhooks in Next.js
Webhooks are the lifeblood of any Shopify app or custom integration. They notify your application when products update, orders are created, or customers update their profiles. But handling them correctly in a modern Next.js environment (especially the App Router) requires passing security checks, handling timeouts, and structuring your code for scale.
The Anatomy of a Shopify Webhook
When an event occurs in a Shopify store, Shopify sends an HTTP POST request to your configured endpoint. This request contains two critical pieces of information:
- Headers: Such as
X-Shopify-Topicand the vitalX-Shopify-Hmac-Sha256. - Body: The JSON payload containing the event data.
If your endpoint doesn't respond with a 200 OK within 5 seconds, Shopify considers it a failure and will retry it later.
Step 1: HMAC Verification in Next.js App Router
You must verify that the webhook actually came from Shopify. You do this by calculating the HMAC hash of the raw request body using your app's shared secret and comparing it to the X-Shopify-Hmac-Sha256 header.
In the App Router (app/api/webhooks/route.ts), you must read the body as text to get the exact raw string before parsing it to JSON.
import crypto from 'crypto';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-shopify-hmac-sha256');
const secret = process.env.SHOPIFY_WEBHOOK_SECRET!;
const hash = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
if (hash !== signature) {
return new NextResponse('Unauthorized', { status: 401 });
}
// Verification passed!
const payload = JSON.parse(rawBody);
// ... process payload ...
}Step 2: Beating the 5-Second Timeout
Because Shopify requires a rapid response, you should not do heavy processing (like complex database queries, ERP syncing, or sending emails) directly inside the API route handler.
For Vercel or standard Node.js deployments, the best practice is to immediately push the payload to a queue (like Inngest, Upstash QStash, or AWS SQS) and return a 200 OK.
Step 3: Idempotency is Mandatory
Shopify webhooks follow "at-least-once" delivery. This means Shopify might send the same webhook twice. Your background worker must be idempotent. Always check if you have already processed the specific X-Shopify-Webhook-Id before running your business logic.
Step 4: Managing Rate Limits
If a merchant updates 1,000 products via CSV, Shopify fires 1,000 webhooks at your endpoint almost simultaneously. If you're doing database writes directly from your webhook handler without an intermediary queue, you will quickly exhaust your connection pool and fail handling webhooks.
Conclusion
Building a durable webhook handler in Next.js requires separating your verification logic from your business logic. By prioritizing rapid responses and pushing the heavy lifting to asynchronous background jobs, you ensure your integration remains highly reliable even under massive load.
Need help with this?
We have built these patterns into production systems for dozens of merchants. See how we can help you implement them.
Related posts
Webhooks: How to Design for Missed Events
Webhooks can and will fail. Learn design patterns that ensure your integration stays reliable even when webhooks are missed or arrive out of order.
The Integration Reliability Checklist
A practical checklist for building reliable integrations. Learn the essential patterns for idempotency, retries, dead-letter queues, and monitoring that prevent production failures.