TheEdoRan / next-safe-action

Type safe and validated Server Actions in your Next.js (App Router) project.

Home Page:https://next-safe-action.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[ASK] Error: Cannot access 'action' before initialization

HubrG opened this issue · comments

Is there an existing issue for this?

  • I have searched the existing issues and found nothing that matches

Library version (optional)

No response

Ask a question

Hello,

First of all, thank you for this repo.

However, I have a problem that I have been trying to solve for several days.

Indeed, on my feature.action file, I use an action(), and I have a synchronization error that pushes the error "Error: Cannot access 'action' before initialization".

Note: when I reload the page, no error is detected. However, as soon as I navigate through my app, this error is systematically detected. I don't know why...

Capture d’écran 2024-03-08 à 09 40 56

The code :

File : safe-action.ts

import { getServerSession } from "next-auth";
import { createSafeActionClient, DEFAULT_SERVER_ERROR } from "next-safe-action";
import { authOptions } from "./next-auth/auth";

export class ActionError extends Error {}

const handleReturnedServerError = async (e: Error) => {
  // If the error is an instance of `ActionError`, unmask the message.
  if (e instanceof ActionError) {
    console.error("Action error:", e.message);
    return e.message;
  }

  // Otherwise return default error message.
  return DEFAULT_SERVER_ERROR;
};

export const action = createSafeActionClient({
  // Can also be an async function.
  handleReturnedServerError,
});

File ("use server") : features.action.ts

"use server";
import {
  HandleResponseProps,
  handleRes,
} from "@/src/lib/error-handling/handleResponse";
import { prisma } from "@/src/lib/prisma";
import { ActionError, action } from "@/src/lib/safe-actions";
import { iFeature } from "@/src/types/iFeatures";
import { updateFeatureSchema } from "@/src/types/schemas/dbSchema";
import { z } from "zod";


export const getFeature = action(z.object({
  id: z.string().cuid(),
}), async ({
  id,
}): Promise<HandleResponseProps<iFeature>> => {
  try {
    const feature = await prisma.feature.findUnique({
      where: { id },
      include,
    });
    if (!feature) throw new ActionError("No feature found");
    return handleRes<iFeature>({
      success: feature,
      statusCode: 200,
    });
  } catch (ActionError) {
    return handleRes<iFeature>({ error: ActionError, statusCode: 500 });
  }
});

Here, the console error :

ReferenceError: Cannot access 'action' before initialization
    at Module.action (./src/lib/safe-actions.ts:4:53)
    at eval (./src/helpers/db/features.action.ts:47:74)
    at (action-browser)/./src/helpers/db/features.action.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1460:1)
    at __webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)
    at eval (./src/helpers/db/plans.action.ts:22:74)
    at (action-browser)/./src/helpers/db/plans.action.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1493:1)
    at __webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)
    at eval (./app/[locale]/admin/classes/stripeManager.ts:5:86)
    at (action-browser)/./app/[locale]/admin/classes/stripeManager.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1249:1)
    at __webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)
    at eval (./src/helpers/functions/stripeCustomerIdManager.ts:12:97)
    at (action-browser)/./src/helpers/functions/stripeCustomerIdManager.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1658:1)
    at __webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)
    at eval (./src/lib/next-auth/auth.ts:8:104)
    at (action-browser)/./src/lib/next-auth/auth.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1757:1)
    at __webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)
    at eval (./src/lib/safe-actions.ts:12:73)
    at (action-browser)/./src/lib/safe-actions.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1801:1)
    at __webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)
    at eval (./src/helpers/db/featuresCategories.action.ts:17:79)
    at (action-browser)/./src/helpers/db/featuresCategories.action.ts (/Users/hubertgiorgi/Sites/starter/.next/server/app/[locale]/(index)/page.js:1482:1)
    at Function.__webpack_require__ (/Users/hubertgiorgi/Sites/starter/.next/server/webpack-runtime.js:33:43)

Do you have any idea where the problem might be coming from? It also seems that the problem is only in this file, as I use other actions in other files, and this error is never detected.

Thanks.

Hubr'

Additional context

Another thing : I call getFeature from a client component un async (click button)

No response

Well, that's pretty weird, and it seems like a TypeScript issue. Can you please provide a link to a repo with a minimal reproduction? Thank you.

Hi @TheEdoRan,

I solved my problem after very long research.

I realized this:

  1. I'm using Next-Auth, and need to check with NextAuth that the user is logged in, check his role etc.

  2. I had imported getServerSession and AuthOption to access this information, but I imported these files at the very top of the safe-action.ts file.

The problem was here...

In reality, the loading of getServerSession wasn't working within the middleware framework, which actually seems quite logical, since it's the principle of middleware to run before everything else. So I tried to import the Next-Auth files inside the middleware and... tadammm! No more errors!

My code :

import { createSafeActionClient, DEFAULT_SERVER_ERROR } from "next-safe-action";

export class ActionError extends Error {}

export const handleReturnedServerError = async (e: Error) => {
  // If the error is an instance of `ActionError`, unmask the message.
  if (e instanceof ActionError) {
    console.error("Action error:", e.message, "Caused by:", e.cause, e.stack);
    return e.message;
  }
  // Otherwise return default error message.
  return DEFAULT_SERVER_ERROR;
};

export const action = createSafeActionClient({
  // Can also be an async function.
  handleReturnedServerError,
});

export const authAction = createSafeActionClient({
  async middleware() {
    const { authOptions } = await import("./next-auth/auth");
    const { getServerSession } = await import("next-auth/next");
    const session = await getServerSession(authOptions);
    if (!session) {
      throw new ActionError("You must be connected");
    } else {
      return session;
    }
  },
  handleReturnedServerError,
});

export const superAdminAction = createSafeActionClient({
  async middleware() {
    const { authOptions } = await import("./next-auth/auth");
    const { getServerSession } = await import("next-auth/next");
    const session = await getServerSession(authOptions);
    if (!session) {
      throw new ActionError("You must be connected");
    } else if (session.user.role !== "SUPER_ADMIN") {
      throw new ActionError("You must be an admin");
    } else {
      return session;
    }
  },
  handleReturnedServerError,
});

export const adminAction = createSafeActionClient({
  async middleware() {
    const { authOptions } = await import("./next-auth/auth");
    const { getServerSession } = await import("next-auth/next");
    const session = await getServerSession(authOptions);
    if (!session) {
      throw new ActionError("You must be connected");
    } else if (
      session.user.role !== "ADMIN" &&
      session.user.role !== "SUPER_ADMIN"
    ) {
      throw new ActionError("You must be an admin");
    } else {
      return session;
    }
  },
  handleReturnedServerError,
});

By the way, I realize that I didn't paste the part of the code that caused this error in my initial message (not knowing it was coming from there, since even "action" (without NextAuth), caused an error).

Once again, thanks for this library !

That's good to hear, thanks for explaining in detail what happened!