Vercel just launched their new waitUntil
utility, and it has cut my response times in half. My requests have gone from ~90ms
to now being ~42ms
on average! The waitUntil method allows you to perform asynchronous tasks, after returning the response to the user in Vercels serverless functions.
This is useful for things like:
- Logs & Analytics: You can log important information about the request and send analytics data to your preferred tracking service without delaying the response to the user.
- Cache Control: If your application relies on caching mechanisms, you can update your cache asynchronously after returning the response. This ensures that the user receives the response quickly while the cache is updated in the background.
- Web Sockets: In real-time applications like Kollabe, you might need to publish WebSocket messages on the server. With waitUntil, you can send these messages asynchronously, allowing the user to receive the response immediately while the messages are published in the background.
What exactly does that mean? Let's take a look.
This is code from my route handler for my website Kollabe.
import { waitUntil } from "@vercel/functions";
import { NextResponse } from "next/server";
const postHandler = async (request: Request, data: VoteRequest) => {
try {
const vote = await prisma.vote.upsert({
where: {
userId_roundId_version: {
userId: request.session.user.id,
roundId: data.roundId,
version: data.version,
},
},
create: {
submission: data.answer,
user: { connect: { id: request.session.user.id } },
},
include: fullVoteInclude,
});
waitUntil(
pushVote(
request.session.user.id,
data.roomId,
data.roundId,
[vote]
),
);
return NextResponse.json({ vote });
} catch (e) {
return NextResponse.json({ Message: e.message, status: 500 });
}
};
export const POST = appRouteAuthWrapper(
appRoutePayloadWrapper<VoteRequest>(postHandler, voteRequest),
);
This captures users voting on tickets. When a user votes, they call my route handler to create that vote. Kollabe is a real-time app, that utilizes web sockets. That means that after successfully saving the user's vote in my persistence layer, I need to publish that vote to the WebSocket.
My client does not care if this part of the request fails. Their data was saved successfully. Previously, you would have to wait for the Websocket call to finish before returning successfully to the user. If you did not wait, the lifecycle of the request would be complete and you would not push your changes to the WebSocket.
await pushVote(
req.session.user.id,
req.body.roomId,
req.body.roundId,
[vote],
);
Not anymore! With Vercels waitUntil
you can simply wrap the asynchronous function and Vercel will keep the function alive before shutting down.
waitUntil(
pushVote(
request.session.user.id,
data.roomId,
data.roundId,
[vote]
),
);
This means that the request does not wait for the WebSocket message to publish. It will return successful to the user, while the WebSocket message is published in the background.
A couple of things that are worth noting:
- This only works with the App router. Unfortunately, if you use the pages router ( like myself ), you would need to change your routes over to use this. I found it worth it in my situation.
- waitUntil will not wait forever. It will inherit the same timeout limits that your functions have.
- This is only if you are hosting on Vercel! This is not a NextJS feature, but a Vercel one. You might even need to install
@vercel/functions
if you aren't already using it. - This is specific to serverless environments.