Github Webhooks And NextJS
I recently decided to switch an internal tool from NextJS pages
router to app router. There’s lots of documentation about how to do that
but the part that I was surprised would be tricky was getting my api
implementation for my Github app’s webhook handler working again.
As part of the migration I moved my webhooks endpoint for github from
its original location /pages/api/github/hooks.ts
to/app/api/github/hooks/route.ts
.
In the app dir, route handlers actually take different parameters and
export different function. For one thing, you export a separate handler
for each method type (though you can double up if they handle the method
validation internally). Secondly, we are no longer receivingNextApiRequest
which is an extension of the IncomingMessage
object. Instead we are receiving the more web-friendly Request
object. Since NextJS now expects handlers that receive aRequest
object we can no longer just export the response
from Probot/Octokit’s createNodeMiddleware
.
So, instead, we have to handle the reception and processing of the
request at a lower level. It looks something like this:
1import { App } from "octokit";
2import { NextRequest } from "next/server";
3import { EmitterWebhookEventName } from "@octokit/webhooks/dist-types/types";
4
5const handler = async (req: NextRequest) => {
6 const app = new App({
7 appId: process.env.APP_ID as string,
8 privateKey: Buffer.from(
9 process.env.PRIVATE_KEY || "",
10 "base64"
11 ).toString("utf8"),
12 webhooks: {
13 secret: process.env.GITHUB_WEBHOOK_SECRET as string,
14 },
15 });
16 app.webhooks.on("pull_request.opened", async ({ payload }) => {
17 try {
18 // Do something
19 } catch (e) {
20 console.error(
21 `pull_request.opened handler failed with error: ${
22 (<Error>e).message
23 }`
24 );
25 }
26 });
27 app.webhooks.on("pull_request.edited", async ({ payload }) => {
28 try {
29 // Do something else
30 } catch (e) {
31 console.error(
32 `pull_request.edited handler failed with error: ${
33 (<Error>e).message
34 }`
35 );
36 }
37 });
38 await app.webhooks.verifyAndReceive({
39 id: req.headers.get("X-GitHub-Delivery") as string,
40 name: req.headers.get("X-GitHub-Event") as EmitterWebhookEventName,
41 signature: req.headers.get("X-Hub-Signature-256") as string,
42 payload: await req.text(),
43 });
44 return new Response("ok");
45};
46
47export { handler as GET, handler as POST };