next.js
next.js copied to clipboard
Parallel server action calls
Link to the code that reproduces this issue
https://github.com/yehonatanyosefi/parallel-routes-example
To Reproduce
- start the application
- click start button
- see console logs come out as ABAB on the server side with two post requests, and on the client side it taking 2 seconds instead of one even though using promise.all
Current vs. Expected behavior
Current behavior of nextjs server actions are they are executed in sequential fashion, however there is no flag or option or a config to let them be executed in parallel. Causing them to block some ux. For example for ai image generation, you'd have to wait for the last call to finish before making your next one, causing you to need to wait over 15 seconds before you can even generate more images instead of the Expected behavior of being able to send them in parallel and stream in finished generations by awaiting them.
In the example I sent you can see even though I used Promise.all on 2 promises that each take a second that should be expected take only 1 second together but because of the sequential nature the current behavior is it takes 2.
Provide environment information
when hosted on vercel it happens, locally it happens too:
Operating System:
Platform: win32
Arch: x64
Version: Windows 11 Pro
Available memory (MB): 32393
Available CPU cores: 20
Binaries:
Node: 20.10.0
npm: N/A
Yarn: N/A
pnpm: N/A
Relevant Packages:
next: 15.0.0-canary.128 // Latest available version is detected (15.0.0-canary.128).
eslint-config-next: N/A
react: 19.0.0-rc-eb3ad065-20240822
react-dom: 19.0.0-rc-eb3ad065-20240822
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
create-next-app, Parallel & Intercepting Routes, Performance
Which stage(s) are affected? (Select all that apply)
next dev (local), Vercel (Deployed)
Additional context
Edit: https://x.com/cramforce/status/1733240566954230063 Above's link to a reply by Vercel's CTO agreeing it should have control if we want it parallel
Server Actions are designed for mutations that update server-side state; they are not recommended for data fetching. Accordingly, frameworks implementing Server Actions typically process one action at a time and do not have a way to cache the return value.
https://react.dev/reference/rsc/use-server#caveats
Server Actions are designed for mutations that update server-side state; they are not recommended for data fetching. Accordingly, frameworks implementing Server Actions typically process one action at a time and do not have a way to cache the return value.
https://react.dev/reference/rsc/use-server#caveats
https://x.com/cramforce/status/1733240566954230063 Your CTO agrees with me
The docs mention that only sequential server action calls are supported, so running them in parallel shouldn't be expected.
I would also agree that it would be very cool to use server actions as an RPC system.
There's this other thread: https://github.com/vercel/next.js/issues/69265 that mentions:
I can confirm this is by design.
Why? To prevent things such as inconsistent states from navigation aborting, client fetching, etc.
But I'm not exactly sure what this means. If a server action is pure (only queries for data), there shouldn't be a problem right? If so, it would be nice if there is a way to mark a server action as a "query" and allow it to run parallel to other queries.
I've been using server actions for queries and it's been a great experience having type safety between frontend and backend, but the sequential nature of them is starting to get slowwww. Would love an option to run them in parallel. Has anyone heard news on this since the CTO's tweet in December 2023?
Similarly, we've used Server Actions for data fetching due to their RPC nature w/ built-in type-safety.
Additional advantages over standard Route Handlers is abstraction away some of the code organization decisions. We don't have to place the Route Handler's route.ts file in a specific place nor consider the endpoint route/path we need to fetch to call it. We don't have to rethink route-based middleware auth and permissions for the call the way we do with adding a new route.ts file. Server Actions take on the same permissions as the page that calls them. Simple.
I think there's definitely a place for full Route Handlers. I also can concede that there is other functionality that benefits from sequential execution of Server Actions, which some projects may want to keep.
It would be nice, however, to either tag a Server Action to not run sequentially or to create a new feature in NextJS with similar syntax to Server Actions but runs in parallel the way Route Handlers do (without requiring all the boilerplate code & code organization that Route Handlers require). Even if this new feature doesn't support the existing features that require Server Actions to remain sequentially executed.
Aside: For my team's use-case, I could even understand a decision to limit it to just GET requests, as opposed to Server Action POST requests, and say it can be used for data fetching and is not suggested for mutations. Basically the opposite of intended use for Server Actions, while maintaining the simplicity and elegant DX of Server Actions.
yes please +1, make this an option or configurable if the default is supposed to be sequential, will be a huge boost to application experience and DX
I found a solution to this annoying problem and managed to pack it in a little utility: icflorescu/next-server-actions-parallel, if you're looking for a ready-made solution. Have a look at the repo, the idea simple enough that you can simply copy-paste it in your own projects if you don't want to add it as a dependency.
I workaround this by returning an obj with a promise (similar to @icflorescu). Like in my server action, I return { promise: (async () => { ... } )() }, and the action returns immediately and the promise can be awaited on the client side like await (await serverAction()).promise. Ugly but it works for concurrent server actions.
I think the real problem here is the DX for route handlers is not great, and lots of users see Server Actions as a way to escape that pain w/o using heavyweight frameworks like tRPC. That's probably the real fix here.
I workaround this by returning an obj with a promise (similar to @icflorescu). Like in my server action, I return
{ promise: (async () => { ... } )() }, and the action returns immediately and the promise can be awaited on the client side likeawait (await serverAction()).promise. Ugly but it works for concurrent server actions.I think the real problem here is the DX for route handlers is not great, and lots of users see Server Actions as a way to escape that pain w/o using heavyweight frameworks like tRPC. That's probably the real fix here.
await (await serverAction()).promise
Thank you for the above suggestion, but if serverAction uses redirect("/some-route") (from next/navigation), an error will occur: Error: NEXT_REDIRECT.
Since redirection happens in a server component, you should move it to the finally block.
Do you have any ideas to fix this error? Thank you.
I support this issue. Over a year later, Next.js 15 has been released, and it's still surprising that it hasn't been resolved.
+1 pls fix
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!