Open k-allagbe opened 1 year ago
I am also facing the same issue.
Please keep in mind that top level await is currently an experimental feature of Next.js, and we will be working on making it more stable in the future 👍
Looking forward to getting this fixed.
Any development on this? We need to make all our forms request a REST API, because our DB library has top level awaits. Is there any known workaround?
@azzzy could maybe make the import async and wrap in react's cache, was a decent workaround for me here: https://github.com/vercel/next.js/issues/58321#issuecomment-1812762598
Any progress on this? Spend a lot of time using server action to do my job, but stuck on this none documented bug, I suppose you should not say server action is stable!
For people that get stuck with this, I had the same issue using SST and Drizzle in a Server Action called client side (both have top level awaits). Fixed it by simply importing my actions file from a server component:
import { initActions } from "@/app/actions";
initActions();
export const initActions = () => {
// This is a no-op function that is called on the server to initialize the actions module.
// Otherwise, Next crashes on top level await when action is called from client component.
};
I'm assuming this loads the module already on the server and means the top level await doesn't happen inside the code path that's broken? Not sure if it will actually work when deployed on Lambda though 😅
Ok, tried deploying on AWS using SST with this trick and it actually does work 👍, even when the lambda is cold, calling the action from the client doesn't crash, as long as the action module has been imported from a server component somewhere.
Would still feel like this should be fixed in Next.js (as in top level await should work in Server Actions called from client components out of the box)
This seems to be fixed with Next.js 14.0.4 🎉!
EDIT: Spoke too fast, I can still reproduce on Next.js 14.0.4.
I am facing the same error when working with Server Actions on Next 14.0.4 and SST. Does Next.js have a plan to support top level await in server actions in the near time?
@adrienbaron Can you elaborate?
I struggle with this.
I have a form that uses useFormState and useFormStatus. The latter needs to be run in a client component.
How can I import the actions in a server component in this case?
I'm also facing this issue. I think it's quite an essential demand to use top level await for initializing databases. Hope Next.js could fix this problem sooner.
I have fixed this by doing the following:
// myAction.ts
'use server'
export async function initAction(){} // literally empty
export async function myNormalActionWithTopLevelAwait(){ // DB Initialisation stuff or any logic depending on top level await libs. }
2. Run that empty action in a server component
// AnyServerComponentSuchAsLayout.tsx import {initAction} from 'myAction.ts'
export const AnyServerComponentSuchAsLayout(){ await initAction() // yes, this does nothing
return (
3. You can now use the action in your client component
// myClientComponent.tsx 'use client' import {myNormalActionWithTopLevelAwait} from 'myAction.ts'
export const MyClientComponent(){
/ You can now use your action here, without the issue of top level await /
}
It's a dirty workaround, but until it gets fixed you can do that.
I think that's what @adrienbaron was suggesting.
I have fixed this by doing the following:
- Export an empty action from the same action file
// myAction.ts 'use server' export async function initAction(){} // literally empty export async function myNormalActionWithTopLevelAwait(){ // DB Initialisation stuff or any logic depending on top level await libs. }
- Run that empty action in a server component
// AnyServerComponentSuchAsLayout.tsx import {initAction} from 'myAction.ts' export const AnyServerComponentSuchAsLayout(){ await initAction() // yes, this does nothing return (<MyPage />) }
- You can now use the action in your client component
// myClientComponent.tsx 'use client' import {myNormalActionWithTopLevelAwait} from 'myAction.ts' export const MyClientComponent(){ /* You can now use your action here, without the issue of top level await */ }
It's a dirty workaround, but until it gets fixed you can do that. I think that's what @adrienbaron was suggesting.
This totally worked and fixed the issue I've been spending hours working around. Thanks!
I'd like to point out that we could use the instrumentation API to initialize DB connections. That may solve most use cases for top level await.
@mordechaim I just tried on my side and it sadly doesn't seem to work (at least locally), the instrumentation hook do get called, and I do import the drizzle orm package, but it still crashes with top level await issue on the server action for some reason 🤷♂️
You shouldn't use top level await entirely. The case for using it is to initialize DB connections, that could be done in the hook calling some init()
function.
@mordechaim some libraries, like Drizzle ORM or SST use top level await in their own code though, the issue occurs because of this, not top level await in my own application code (I don't have any). Top level await is a standard JS feature these days, and Next supports it in most places, so it not being supported in this case feels more like a bug 🤷♂️.
I have tons of server actions depends on that one top level await (db connection), so I don't think adding init functions for every one of them is not going to be easy. Is there any plans to solve this issue in soon future?
I have fixed this by doing the following:
- Export an empty action from the same action file
// myAction.ts 'use server' export async function initAction(){} // literally empty export async function myNormalActionWithTopLevelAwait(){ // DB Initialisation stuff or any logic depending on top level await libs. }
- Run that empty action in a server component
// AnyServerComponentSuchAsLayout.tsx import {initAction} from 'myAction.ts' export const AnyServerComponentSuchAsLayout(){ await initAction() // yes, this does nothing return (<MyPage />) }
- You can now use the action in your client component
// myClientComponent.tsx 'use client' import {myNormalActionWithTopLevelAwait} from 'myAction.ts' export const MyClientComponent(){ /* You can now use your action here, without the issue of top level await */ }
It's a dirty workaround, but until it gets fixed you can do that. I think that's what @adrienbaron was suggesting.
I'm using SST and have the same problem and this worked as well (Thank you!), but it's ugly as hell. Hope this gets fixed soon.
I have tons of server actions depends on that one top level await (db connection), so I don't think adding init functions for every one of them is not going to be easy. Is there any plans to solve this issue in soon future?
One way to solve this, is to create one async function called eg. initActions and place every initializer inside, and only call initActions in eg. layout component. That way its easier to maintain IMO.
Here is the example code that I am using:
src/app/_initActions.ts
export const initActions = async () => {
await initProductActions();
await initCategoryActions();
...more initActions
};
and in my src/app/layout.tsx
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
await initActions();
return (<App/>);
}
Is there any update on this? I use SST + nextjs with server actions running from client side and i still get the error with next@^14.2.5
we tried for first time server actions (and i think not developed for any, but for symple post form actions)
new type await function like db connect and other, not work property:
⨯ SyntaxError: await is only valid in async functions and the top level bodies of modules
at (action-browser)/./helpers/database.mongo.js (W:\clienti\project\.next\server\app\agents\[ACCDAG]\page.js:652:1)
at __webpack_require__ (W:\clienti\project\.next\server\webpack-runtime.js:33:43)
at eval (./helpers/collections.js:8:81)
at (action-browser)/./helpers/collections.js (W:\clienti\project\.next\server\app\agents\[ACCDAG]\page.js:608:1)
at __webpack_require__ (W:\clienti\project\.next\server\webpack-runtime.js:33:43)
at eval (./helpers/server.actions.js:10:78)
at (action-browser)/./helpers/server.actions.js (W:\clienti\project\.next\server\app\agents\[ACCDAG]\page.js:685:1)
at Function.__webpack_require__ (W:\clienti\project\.next\server\webpack-runtime.js:33:43)
problem: file that importend in function file helpers:
import {MongoClient, ObjectId} from "mongodb";
/**
*
* @param _id
* @return {ObjectId}
* @private
*/
export const Id = (_id) => new ObjectId(_id)
/**
*
* @type {*|string}
*/
const connectionString = process.env.ATLAS_URI || "";
/**
*
* @type {MongoClient}
*/
const client = new MongoClient(connectionString);
/**
*
* @type {MongoClient}
*/
const conn = await client.connect();
/**
*
* @type {Db}
*/
const database = conn.db();
export default database;
file server actions example:
'use server'
import {first} from "@/helpers/database";
import {generatePassword} from "@/helpers/global";
import {agents} from "@/helpers/collections";
// https://github.com/vercel/next.js/issues/54282
export async function createAgentOnDatabase(AGCDAG) {
// toto: perform query or other
// return to client data
}
For us, this mongo.db helper, work in many project, 100/20 project of in many framework.
only server actions not work.
UPDATE | ASYNC DB COLLECTION WORK FINE |
WE HAVE WRITE NEW DATABASE HELPER, NOW WORK PERFECT FOR SERVER ACTIONS. (MONGO DB)
database.actions.js || helper only for actions
import {Db, MongoClient, ObjectId} from "mongodb";
/**
/**
/**
/**
@return {Promise
try { const conn = await client.connect(); database = conn.db(); return database; } catch (error) { console.error("Errore durante la connessione al database:", error); throw error; } }
export const collection = async (name) => {
const db = await mongo()
return db.collection(name);
}
>form.actions.js
```js
'use server'
import {collection} from "@/actions/database.actions";
/**
*
* @param formData
* @returns {Promise<{success: boolean}>}
*/
export async function createPlaylist(formData) {
const playlists = await collection('playlists')
const name = formData.get('name')
const tags = formData.get('tags')
const description = formData.get('description')
const cover = formData.get('cover')
const png = formData.get('png')
// EXAMPLE INSERT DOCUMENT
await playlists.insertOne({name, tags, description, cover, png})
return {success: true};
}
FormCreate.jsx
'use client'
import useFormValues from "@/hooks/useFormValues"; import {createPlaylist} from "@/actions/form.actions";
export default function FormCreate() {
const {formData, formValues, setFormValues} = useFormValues()
const handleSubmit = async (e) => {
e.preventDefault();
await createPlaylist(formData);
}
return (
<>component code jsx</>
)
In the meantime we await a solution, I hope it helps 👌
I've also been struggling with this and I figured before spending time implementing a workaround I would at least try upgrading to Next 15 RC and see if the problem still exists or if I really just need a short term workaround. The good news is the compilation seems to work just fine in my use case (SST + drizzle) :partying_face:.
Unfortunately it seems like we have some dependencies that aren't ready for next 15 / react 19 because I get some runtime errors thrown inside some libs when loading up the page. Because of this I can't confirm that there isn't some kind of runtime bug that still exists but at least the compilation seems good. If I have some time I'll test this out in a minimal example, but it seems like time is a hard thing to come by lately :sweat_smile:.
Anyway hopefully some of this will turn stable in the near future and we'll all be able to clean up our workarounds!
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
Link to the code that reproduces this issue or a replay of the bug
https://github.com/k-allagbe/my-app-canary.git
To Reproduce
Describe the Bug
The provided code is an example of using Top Level Await in a file containing Server Actions called from a Client Component. This is relevant because, my actions might depend on frameworks that use top level awaits.
The result shows that calling Server Actions from Client Components doesn't work when the Server Action file involves top level await.
The problem is not observed with Server Components.
Expected Behavior
I expected to be able to call my Server Action from a Client Component even if a Top Level Await is involved in the Server Action file, because I might use some frameworks that rely on Top Level Awaits.
Which browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
No response
NEXT-1540