javascript 49 lines · 8 steps

A GitHub webhook that revalidates Next.js docs

A Route Handler verifies a signed push event and revalidates only the doc paths whose MDX files actually changed.

Explained by highlit
1import { NextResponse } from 'next/server';
2import { revalidatePath } from 'next/cache';
3import crypto from 'node:crypto';
4 
5function isValidSignature(rawBody, signature) {
6 if (!signature) return false;
7 const hmac = crypto.createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET);
8 const digest = `sha256=${hmac.update(rawBody).digest('hex')}`;
9 const expected = Buffer.from(digest);
10 const received = Buffer.from(signature);
11 return expected.length === received.length && crypto.timingSafeEqual(expected, received);
12}
13 
14export async function POST(request) {
15 const rawBody = await request.text();
16 const signature = request.headers.get('x-hub-signature-256');
17 
18 if (!isValidSignature(rawBody, signature)) {
19 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
20 }
21 
22 const event = request.headers.get('x-github-event');
23 if (event === 'ping') {
24 return NextResponse.json({ ok: true });
25 }
26 if (event !== 'push') {
27 return NextResponse.json({ skipped: event }, { status: 202 });
28 }
29 
30 const payload = JSON.parse(rawBody);
31 const touched = new Set();
32 for (const commit of payload.commits ?? []) {
33 for (const file of [...commit.added, ...commit.modified, ...commit.removed]) {
34 const match = file.match(/^content\/docs\/(.+)\.mdx$/);
35 if (match) touched.add(`/docs/${match[1]}`);
36 }
37 }
38 
39 if (touched.size === 0) {
40 return NextResponse.json({ revalidated: [] });
41 }
42 
43 revalidatePath('/docs');
44 for (const path of touched) {
45 revalidatePath(path);
46 }
47 
48 return NextResponse.json({ revalidated: [...touched], now: Date.now() });
49}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Always verify webhook signatures with a constant-time comparison to avoid timing attacks.
  2. 2Read the raw request body before parsing so the bytes you verify match the bytes GitHub signed.
  3. 3Revalidate only the paths that actually changed instead of blowing away the whole cache.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A GitHub webhook that revalidates Next.js docs — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code