next-server-actions-parallel
v1.2.1
Published
Run Next.js server actions in parallel. Like tRPC, but without the boilerplate.
Downloads
906
Maintainers
Readme
Parallel Next.js Server Actions
Run multiple Next.js server actions in parallel.
The missing ingredient to build tRPC-like workflows, but without the boilerplate.
TL;DR
Install:
pnpm i next-server-actions-parallel
Define your server actions like so:
app/page.actions.ts
:
'use server';
import { createParallelAction } from 'next-server-actions-parallel';
export const listUsers = createParallelAction(async () => { // 👈 don't forget the `async` keyword!!
return prisma.user.findMany(); // 👈 let's assume this takes 3 seconds
});
export const listProducts = createParallelAction(async (categoryId: string) => {
return prisma.product.findMany({ where: { categoryId } }); // 👈 let's assume this also takes 3 seconds
});
Call them like so:
app/page.tsx
:
'use client';
import { runParallelAction } from 'next-server-actions-parallel';
import { listUsers, listProducts } from './page.actions';
export default async function Page() {
// 👇 this may take slightly more than 3 seconds, but a lot less than 6.
const [users, products] = await Promise.all([
runParallelAction(listUsers()),
runParallelAction(listProducts('82b2ab20-ec1e-4539-85a2-ea6737555250')),
]);
return (
<div>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
How does it perform?
In a nutshell: a lot faster than default Next.js server actions, not as fast as REST API routes.
Check it in your specific deployment environment and decide if it makes sense for your project.
Show gratitude
If you find my open-source work useful, please consider sponsing me on GitHub Sponsors.
Background story
When Vercel added support for server actions in Next.js, a lot of developers - myself included - were hyper-excited about it and saw it as a boilerplate-free alternative to tRPC.io to Telefunc.
Many were however disappointed to find that Next.js server actions were only executed in series, contrary to what one would expect when triggering them in parallel with Promise.all
.
It probably worked like that because they were intended to be used for mutations, because "data should be fetched in server components or using REST API endpoints".
Plus, since server actions are implemented with POST
requests, some purists are reluctant to the idea of using them for fetching data, though POST
requests can and usually do return data.
However, there are many reasons why projects like tRPC.io, Telefunc and other *RPCs were built and have no problem using POST
requests to fetch data. If you're building real, data-rich applications, including something like tRPC.io in your stack gives you type-safety and spares you the effort of building and maintainings maybe hundreds of API endpoints just to populate dynamic UI components (such as autocompletes and selects).
tRPC.io is the "batteries-included" choice; I'm a big fan and former contributor to the ecosystem - see tRPC-SvelteKit, but I always found the amount of boilerplate to be a bit discouraging for new developers and/or small projects. Telefunc looks like a nice alternative, but it doesn't (yet, as of November 2024) have an obvious way of integrating with the Next.js app router.
It would be nice to use server actions for RPC without the current limitation of being unable to call multiple functions in parallel.
Here's a non-exhaustive list of issues and discussions where the subject was mentioned:
- https://github.com/vercel/next.js/discussions/50743
- https://github.com/vercel/next.js/issues/69265
- https://x.com/cramforce/status/1733240566954230063
- https://stackoverflow.com/questions/77484983/why-are-server-actions-not-executing-concurrently
- https://stackoverflow.com/questions/78548578/server-actions-in-next-js-seem-to-be-running-in-series
Well, that's what next-server-actions-parallel is for.
How does it work?
It's actually quite simple:
- The
createParallelAction
is wrapping the promise your server action returns in an array (a single-ellement tuple, to be more precise - see note below) and returning that array instead of the result of the promise. - The
runParallelAction
client-side utility function will await the promise and return the result.
This repository contains a light Next.js app that demonstrates it in action and benchmaks it against REST API routes and default server actions, so you can check it out in different environments and decide if it makes sense for your project.
Note:
Wrapping the promise in an object would also work, but I've chosen to use an array for data-transfer efficiency.
Do I actually need to use this library?
Not necessarily. The two functions that are exported by the library are small enough to be easily copied and pasted into your own project, see source code here.
But since this is a common pain point for many developers, I've decided to provide a simple and easy-to-use wrapper... And maybe hope that if you find it useful you won't hesitate to show your eternal gratitude by throwing a few greenbacks my way , at least for discovering the trick... 😜
License
The MIT License.