Skip to content

Repository files navigation

GitHub webhook activity feed

A small Next.js app that receives GitHub webhooks and shows them as a live list in the browser. Clone it, run it on your computer, point one repo at it, and watch stars, issues, pushes, and other events appear.

This is a learning / playground project. Events are stored in memory (they disappear when you stop the server). It is not meant for production.

Note that this will only catch the GitHub events that happen while your server is running. In the "real world", you would have a server running that would be listening for these. This is just a test/demo app and we won't have the server running 24/7.

How it works

A webhook is GitHub calling you. Your app does not poll GitHub. When something happens on a repo, GitHub POSTs JSON to a URL you registered.

GitHub  --POST-->  public HTTPS URL (smee)
                      |
                      v
                 smee client on your computer
                      |
                      v
         POST /api/webhooks/github  (Next.js)
                      |
                      +-- verify HMAC signature (WEBHOOK_SECRET)
                      +-- ignore events from other repos
                      +-- store a short summary in memory
                      |
                      v
         GET /api/events  <-- the page keeps one SSE connection open

smee is a webhook relay for local development. GitHub will only POST to a public HTTPS URL; it cannot reach http://localhost:3000 on your computer. smee.io gives you a public channel URL to paste into GitHub, and npm run smee runs a client that forwards those payloads to Next.js. Without smee (or another tunnel), webhook deliveries never arrive.

The page also saves one owner/repo via POST /api/settings. Non-ping events are stored only if repository.full_name matches that pair (letter case is ignored). ping (GitHub’s “does this URL work?” event) is always stored.

Setup

You need Node.js 20.9+ and a GitHub repo you can add webhooks on.

git clone https://github.com/kevinsmithwebdev/wh-test.git
cd wh-test
npm install
cp .env.example .env.local

On Windows Command Prompt use copy .env.example .env.local instead of cp.

.env.local is gitignored. Variables:

Variable Purpose
WEBHOOK_SECRET Shared secret. GitHub signs each POST; the app rejects unsigned or wrongly signed bodies with 401. Required by this app.
SMEE_URL Your smee.io channel. Used by npm run smee, not by Next.js.

The example value dev-secret is fine for local play. Use the same string in GitHub’s webhook Secret field. Change it if you want; then restart npm run dev.

smee channels are public: anyone with the URL can POST to it. Signature verification is what keeps random payloads out, so the GitHub Secret and WEBHOOK_SECRET must match.

Run it

You need two terminals.

Terminal 1 — the app

npm run dev

Open http://localhost:3000. Enter the owner and repository name you will watch, then Save — do this before starring or pushing, or those events are ignored.

Terminal 2 — the tunnel

  1. Open https://smee.io and click Start a new channel. Copy the URL (https://smee.io/...).
  2. Each person needs their own channel. Do not reuse someone else’s.
  3. Put that URL in .env.local as SMEE_URL.
npm run smee

Leave both processes running.

Connect one GitHub repo

  1. On that repo: Settings → Webhooks → Add webhook (admin access required).
  2. Fill in:
Field Value
Payload URL Your smee channel only (https://smee.io/...). Not localhost, and not /api/webhooks/github — smee already forwards to that path.
Content type application/json
Secret Same as WEBHOOK_SECRET in .env.local
SSL verification Enable
Which events Send me everything
Active Checked
  1. Click Add webhook. GitHub sends a ping immediately.

You should see a green delivery under Recent Deliveries, a log line in the smee terminal, and a Webhook ping card on the page.

If the secret was wrong, edit the webhook, set Secret correctly, Update webhook, then Recent Deliveries → ping → Redeliver.

Try a real event

Star the repo, open an issue, or push a commit. A new card should appear as soon as the webhook arrives.

If GitHub shows 200 but the page does not update, the payload was ignored: Watching owner/repo on the page must match repository.full_name in the delivery (letter case does not matter). The Next.js terminal logs ignored deliveries. Save the form again if you restarted the app (settings are in memory too).

Test the API without GitHub

With npm run dev running:

npm run example:ping

That sends a signed ping to http://127.0.0.1:3000/api/webhooks/github. You should get 200 {"ok":true} and a ping card. This avoids shell quoting issues when computing HMAC signatures.

Project layout

Path Role
app/page.tsx UI: repo form + event list
app/api/settings/route.ts GET/POST the watched owner/repo
app/api/events/route.ts GET SSE stream of stored events
app/api/webhooks/github/route.ts POST receiver
lib/github.ts Signature check + human-readable summaries
lib/events.ts / lib/settings.ts In-memory stores
hooks/useRepoSettings.ts / hooks/useEvents.ts Client state + live event stream
scripts/load-env.mjs Shared .env.local loader for smee and example:ping
.env.example Template for .env.local

Limits (on purpose)

  • One repo at a time (the one you type in the form).
  • Events and settings vanish when Next.js restarts.
  • The app only receives events while both Next.js and smee are running. GitHub will retry failed deliveries for a while, then stop.
  • Do not deploy this as-is: serverless hosts do not share that in-memory store.

About

web hooks test - just a test app to learn web hooks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages