Skip to content

Instantly share code, notes, and snippets.

@jpvalery
Last active October 7, 2023 08:59
Show Gist options
  • Save jpvalery/643de66ea7fa6755f29a8fca63bc09a4 to your computer and use it in GitHub Desktop.
Save jpvalery/643de66ea7fa6755f29a8fca63bc09a4 to your computer and use it in GitHub Desktop.
How to do fire-and-forget requests in NextJS using middleware (Slack FaaS serverless ack() alternative)

Many thanks and most of the credit to Delba over at Vercel!

We were able to find a way to return a response from a first endpoint and then wait for a second endpoint to complete.

This stemmed from Slack requiring a response within 3000ms—but if your endpoint takes longer (for instance if it needs to fetch data from another third-party), this results in a timeout message in Slack.

This leverages middleware.ts with some regex to work on any path ending in /incoming and then routing the request to /worker.

// path /api/folder/incoming.js
export default async function handler(req, res) {
let message = `hi from /api/folder/incoming`;
console.log(message);
console.log(req.body)
res.status(200).send(message);
return;
}
// path /api/folder/worker.js
export default async function handler(req, res) {
let message = `hi from /api/folder/worker`;
// long running task
await new Promise((resolve) => setTimeout(resolve, 3000));
console.log(message);
console.log(req.body);
res.status(200).send(message);
return;
}
import { NextResponse } from 'next/server';
// Config is for all API routes that end in /incoming
export const config = {
matcher: '\/api\/(.*?)\/incoming',
};
export default async function middleware(
req,
event
) {
// We define pattern and path to avoid any TS errors
const pattern = new RegExp('\/api\/(.*?)\/incoming');
const path = req.nextUrl.pathname;
// if the path matches the pattern, we return a 200 and then let the worker do its thing, however long that takes
if (pattern.test(path)){
// We replace /incoming by /worker as it's our pattern
const workerPath = path.replace('/incoming', '/worker')
// We wait until the worker endpoint completes
event.waitUntil(
fetch(`http://localhost:3000/${workerPath}`, {
method: "POST",
body: JSON.stringify(Object.fromEntries(await req.formData())),
})
);
// We return a response
return NextResponse.next();
}
}
@hamzawaleed0102
Copy link

Hi @jpvalery, thanks for putting it here. Are you able to use it with the slack bolt SDK?
Here's how I tried it but not able to make it work so far:

  1. When slack hits my /incoming endpoint, it triggers above given middleware and req.formData throws an exception that it only supports url-encoded and form-data content-type headers while slack sent application/json I guess 🤔

  2. In fetch() I was hitting a /bolt endpoint which initialises bolt app - it hits the endpoint succesfuly (logs appear) BUT bolt app doesn't handle the slack payload and hence no slack event/action/listener gets triggered.

Am I missing something here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment