Test Hygraph webhooks locally
Receive Hygraph webhook events on localhost with a Horizon tunnel, and verify the gcms-signature header.
Receive Hygraph events on your laptop while you build, with a URL Hygraph can reach.
Before you begin
- Node.js 18 or later
- A Horizon account and the CLI (see Getting started)
- A reserved subdomain for
-s. Reserve one on the Subdomains page. - A Hygraph project where you can manage webhooks
Start your app
When a webhook has a secret key, Hygraph sends a gcms-signature header. It looks like sign=<signature>, env=<environment>, t=<timestamp>. The signature is an HMAC SHA-256 (a keyed hash) of a JSON payload built from the body, the environment name and the timestamp.
Hygraph's @hygraph/utils package does that work. Install it.
npm install @hygraph/utilsRead the raw body with request.text() and pass it in unchanged.
import { verifyWebhookSignature } from "@hygraph/utils";
export async function POST(request: Request) {
const secret = process.env.HYGRAPH_WEBHOOK_SECRET;
if (!secret) {
return new Response("Missing HYGRAPH_WEBHOOK_SECRET", { status: 500 });
}
const body = await request.text();
const signature = request.headers.get("gcms-signature") ?? "";
const isValid = verifyWebhookSignature({ body, signature, secret });
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}
const { operation, data } = JSON.parse(body);
console.log(`Received Hygraph ${operation} for ${data.__typename}`);
return new Response("ok", { status: 200 });
}Pick a secret and store it in an environment variable. Use a random, high-entropy string.
HYGRAPH_WEBHOOK_SECRET=replace-with-a-long-random-stringStart the app on port 3000.
npm run devStart a tunnel
Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so your Hygraph webhook would point at a dead URL after a restart. Reserved subdomains are a paid feature, see Pricing.
hrzn tunnel http://localhost:3000 -s my-appHORIZON: Tunnel connected
URL https://my-app.hrzn.run (reserved)
Forwarding http://localhost:3000
Request log https://hrzn.run/dashboard/tunnels/my-appYour public URL is https://my-app.hrzn.run. Keep this terminal open.
Add the webhook in Hygraph
- Open Project Settings, then Automation, then Webhooks.
- Select Add webhook.
- Enter a name and a description.
- Turn on the option to include the payload.
- Enter
https://my-app.hrzn.run/api/webhooks/hygraphas the URL. - Select the content model, the stage and the action you want to listen for.
- Enter the same value as
HYGRAPH_WEBHOOK_SECRETin the secret key field. - Select Add webhook.
Trigger an event
Hygraph's docs describe no test button and no resend. Trigger a real event instead: create, update or publish an entry of the content model you selected.
To inspect a call, select View logs next to the webhook. Each entry shows the timestamp, the HTTP status, the content model, the action and the duration. Select an entry to see the request and response. Hygraph keeps logs for 7 days.
Check it works
Publish an entry. Horizon prints one line for the request:
POST 200 /api/webhooks/hygraphYour app terminal prints the operation and the model name, for example:
Received Hygraph publish for PostIf the line shows [401], see Troubleshooting.
Troubleshooting
The signature doesn't match
- Check that
HYGRAPH_WEBHOOK_SECRETis identical to the secret key on the webhook, with no extra spaces or newline. - Pass the raw body to
verifyWebhookSignature. Don't runJSON.parseandJSON.stringifyfirst, because that can change the bytes. - Restart
npm run devafter you edit.env.local.
The handler returns 401 and gcms-signature is empty
Hygraph only sends the header when the webhook has a secret key. Edit the webhook and add one.
The URL changed after a restart
You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app and the URL stays the same. -s needs a subdomain you reserved, see Pricing.
Nothing reaches your app
- Check that the Horizon terminal is still running. If its last line is
Connection lost. Reconnecting…, wait forReconnected. - Check that the webhook URL ends with
/api/webhooks/hygraph. - Open View logs to see whether Hygraph sent the call.
Next steps
- Read Hygraph's webhooks reference.