Next.js Discord

Discord Forum

await is only valid in async functions and the top level bodies of modules

Unanswered
Blue Picardy Spaniel posted this in #help-forum
Open in Discord
Avatar
Blue Picardy SpanielOP
import { createServerActionProcedure } from "zsa";
import { getUser } from "@/lib/auth";

export const authenticatedAction = createServerActionProcedure().handler(
  async () => {
    const user = await getUser();
    if(!user) return null;

    return { user };
  }
);

"use server";

import { z } from "zod";
import { authenticatedAction } from "@/utils/server-only";
import { GuildCollection } from "@/lib/db";
import { ratelimitByKey } from "@/lib/ratelimiter";

export const saveModuleToggleAction = authenticatedAction
  .createServerAction()
  .input(
    z.object({
      guildId: z.string(),
      module: z.string(),
      enabled: z.boolean()
    })
  )
  .handler(async ({ input, ctx: { user } }) => {
    const canAccess = await ratelimitByKey(`settings-${user._id}`, 10, 10);
    if(!canAccess) return { error: "You are being rate limited." };

    console.log("Success");
  });

"use client";

import { Button } from "@mantine/core";
import { useServerAction } from "zsa-react";
import { saveModuleToggleAction } from "@/app/dashboard/[guild_id]/actions";

export default function ModuleWrapper() {
  const { execute: saveModuleToggle } = useServerAction(saveModuleToggleAction);

  function enableModule() {
    saveModuleToggle({
      guildId: guild.id,
      module: moduleName,
      enabled: true
    });
  }

  return (
    <Button onClick={enableModule}>
  );
}

GET /dashboard/1160247290225754204/server-backups 200 in 232ms

⨯ SyntaxError: await is only valid in async functions and the top level bodies of modules
    at (action-browser)/./src/lib/db.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:1099:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/lib/api/discord.ts:9:65)
    at (action-browser)/./src/lib/api/discord.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:989:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/utils/server-only.ts:8:74)
    at (action-browser)/./src/utils/server-only.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:1317:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/app/dashboard/[guild_id]/actions.ts:10:76)
    at (action-browser)/./src/app/dashboard/[guild_id]/actions.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:534:1)
    at Function.__webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
digest: "2299097498"

POST /dashboard/1160247290225754204/server-backups 500 in 41ms

Any ideas? When I remove the part where I invoke saveModuleToggle(), the error no longer occurs.

66 Replies

Avatar
⨯ SyntaxError: await is only valid in async functions and the top level bodies of modules
at (action-browser)/./src/lib/db.ts
where is your db.ts?
can u share your code
this sounds like some weird typescript config error
Avatar
Blue Picardy SpanielOP
it's unrelated but sure
import type { Collection } from "mongodb";
import { MongoClient } from "mongodb";
import { env } from "@/env";
import type { DbGuild, DbSession, DbUser } from "@/types/db";

// Connect

const client = new MongoClient(env.DATABASE_URL);
await client.connect();

// Schemas

const db = client.db();

export const UserCollection = db.collection("users") as Collection<DbUser>;
export const SessionCollection = db.collection("sessions") as Collection<DbSession>;
export const GuildCollection = db.collection("guilds") as Collection<DbGuild>;
Avatar
it is related
Avatar
Blue Picardy SpanielOP
alright
Avatar
await client.connect();

wrap that function in a async function
and trigger on MongoClient callback
Avatar
Blue Picardy SpanielOP
hm okay
import type { Collection } from "mongodb";
import { MongoClient } from "mongodb";
import { env } from "@/env";
import type { DbGuild, DbSession, DbUser } from "@/types/db";

// Connect

const client = new MongoClient(env.DATABASE_URL);

(async () => {
  client.connect().then(() => console.log("connected"));
})();

// Schemas

const db = client.db();

export const UserCollection = db.collection("users") as Collection<DbUser>;
export const SessionCollection = db.collection("sessions") as Collection<DbSession>;
export const GuildCollection = db.collection("guilds") as Collection<DbGuild>;
after doing that im now getting an error in another file..
⨯ src\lib\cache.ts (4:16) @ eval
 ⨯ ReferenceError: await is not defined
import { createClient } from "redis";
import { env } from "@/env";

const client = await createClient({
  socket: {
    port: 6379,
    host: env.SERVER_IP
  },
  password: env.REDIS_PASSWORD
}).connect();

export default client;
but what i dont understand is both of these were fine earlier. literally removing
saveModuleToggle({
      guildId: guild.id,
      module: moduleName,
      enabled: true
    });
fixes everything
Avatar
what?
the function?
Avatar
Blue Picardy SpanielOP
yeah
Avatar
yeah ofc
cause that function triggers your db
wait a sec
Avatar
Blue Picardy SpanielOP
no it communicates with my cache
Avatar
u typically want to save your db instance and return that with a singleton
so u have a getter
for your db
Avatar
Blue Picardy SpanielOP
i removed the db logic for debugging
i wanted a minimal repro
if i remove the cache stuff in the function and just add a console.log, i still get the error
Avatar
what error and where
Avatar
Blue Picardy SpanielOP
export const saveModuleToggleAction = authenticatedAction
  .createServerAction()
  .input(
    z.object({
      guildId: z.string(),
      module: z.string(),
      enabled: z.boolean()
    })
  )
  .handler(async ({ input, ctx: { user } }) => {
    const canAccess = await ratelimitByKey(`settings-${user._id}`, 10, 10);
    if(!canAccess) return { error: "You are being rate limited." };

    console.log("Success");
  });

if i remove await ratelimitByKey which communicates with the cache (where my [2nd error](https://nextjs-forum.com/post/1282090770345496586#message-1282093447297106052) is coming from), i still get the same [original error](https://nextjs-forum.com/post/1282090770345496586#message-1282090770345496586)
but if i don't invoke saveModuleToggleAction() then i simply don't get the error anymore
im just trying to use server actions via a library called zsa which im new to, so im guessing im doing something wrong with that library, especially since if i dont invoke the function then the errors dont appear anymore
Avatar
send your error log
the current error that u face
Avatar
Blue Picardy SpanielOP
POST /dashboard/1160247290225754204/server-backups 500 in 149ms
 ⨯ src\lib\cache.ts (4:16) @ eval
 ⨯ ReferenceError: await is not defined
    at eval (./src/lib/cache.ts:10:16)
    at (action-browser)/./src/lib/cache.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:1077:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/lib/api/discord.ts:10:68)
    at (action-browser)/./src/lib/api/discord.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:989:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/utils/server-only.ts:8:74)
    at (action-browser)/./src/utils/server-only.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:1317:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/app/dashboard/[guild_id]/actions.ts:10:76)
    at (action-browser)/./src/app/dashboard/[guild_id]/actions.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:534:1)
    at Function.__webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
digest: "2769027943"
  2 | import { env } from "@/env";
  3 |
> 4 | const client = await createClient({
    |                ^
  5 |   socket: {
  6 |     port: 6379,
  7 |     host: env.SERVER_IP
connected
connected
Avatar
ah i see
so
try this:
const client = redis.createClient({
    socket: {
      host: "ip",
      port: 6969,
    },
    password: "your pw",
  });

  try {
    await client.connect();
  } catch (error: any) {
    console.error(error);
  }
Avatar
Blue Picardy SpanielOP
done
GET /dashboard/1160247290225754204/server-backups 200 in 2518ms
 ⨯ SyntaxError: await is only valid in async functions and the top level bodies of modules
    at (action-browser)/./src/lib/cache.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:1077:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/lib/api/discord.ts:10:68)
    at (action-browser)/./src/lib/api/discord.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:989:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/utils/server-only.ts:8:74)
    at (action-browser)/./src/utils/server-only.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:1317:1)
    at __webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
    at eval (./src/app/dashboard/[guild_id]/actions.ts:10:76)
    at (action-browser)/./src/app/dashboard/[guild_id]/actions.ts (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\app\dashboard\[guild_id]\server-backups\page.js:534:1)
    at Function.__webpack_require__ (C:\Users\ethan\Desktop\Projects\watchdog\website\.next\server\webpack-runtime.js:33:43)
digest: "1821699749"
 POST /dashboard/1160247290225754204/server-backups 500 in 52ms
connected
connected
Avatar
are you directly using client somewhere?
wrap your createClient in a seperate function have that return your client
and await that function
Avatar
Blue Picardy SpanielOP
yes im importing from my cache file
Avatar
let client;

async function createRedisClient() {
    const client = createClient({
      socket: {
        host: "ip"
        port: 6969
      },
      password: "pw",
    });

    await client.connect();
    return client;
}

export async function getRedisClient() {
  if (!client) {
    client = await createRedisClient();
  }
  return client;
}

@Blue Picardy Spaniel
Avatar
Blue Picardy SpanielOP
if i do const cachedGuilds = await getRedisClient().get(key); it returns Property 'get' does not exist on type 'Promise<any>'.
Avatar
try giving it a type
Avatar
Blue Picardy SpanielOP
of what
Avatar
idk u need to check
Avatar
Blue Picardy SpanielOP
Image
Avatar
u see it
RedisClientType
Avatar
Blue Picardy SpanielOP
Image
i genuinely dont think redis or mongodb are the issue.. if i remove my redis implementation and just make it like this:

"use server";

import { z } from "zod";
import { authenticatedAction } from "@/utils/server-only";

export const saveModuleToggleAction = authenticatedAction
  .createServerAction()
  .input(
    z.object({
      guildId: z.string(),
      module: z.string(),
      enabled: z.boolean()
    })
  )
  .handler(async ({ input, ctx: { user } }) => {
    console.log("Success");
    return { success: true };
  });

then i still get [the original error](https://nextjs-forum.com/post/1282090770345496586#message-1282090770345496586). so this is nothing to do with any of that as it all works fine. the issue is something to do with zsa or zsa-react as when i remove the zsa invocation, everything works fine
Avatar
well
the call stack shows something different
i dont think its lying to you xD
?
u need to set the return type for your getter
dont give the client a type
cause RedisClientType is a promise
and your client cant have that in the initial value
Avatar
Blue Picardy SpanielOP
@gin yo u were right, sorry for doubting you
still not sure why top level await isnt working when i use server actions tho
Avatar
cause thats a new feature for javascript
i told u how to fix this issue above
well its not a issue
Avatar
Blue Picardy SpanielOP
yea