Next-Auth Session in Server Function

Trying out the new app directory with an example project and ran into issues with accessing the user session within a server function via getServerSession
"use server";

import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import prisma from "@/db";
import { EventPrivacy } from "@prisma/client";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export async function createEvent(formData: FormData) {
const session = await getServerSession(authOptions);
if (!session) return;

const event = await prisma.event.create({
data: {
image: formData.get("imageUrl") as string,
name: formData.get("name") as string,
description: formData.get("description") as string,
startDate: new Date(formData.get("startDate") as string),
endDate: new Date(formData.get("endDate") as string),
privacy: formData.get("privacy") as EventPrivacy,
createdById: session.user.id
}
});
redirect(`/event/${event.id}`);
}
"use server";

import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import prisma from "@/db";
import { EventPrivacy } from "@prisma/client";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export async function createEvent(formData: FormData) {
const session = await getServerSession(authOptions);
if (!session) return;

const event = await prisma.event.create({
data: {
image: formData.get("imageUrl") as string,
name: formData.get("name") as string,
description: formData.get("description") as string,
startDate: new Date(formData.get("startDate") as string),
endDate: new Date(formData.get("endDate") as string),
privacy: formData.get("privacy") as EventPrivacy,
createdById: session.user.id
}
});
redirect(`/event/${event.id}`);
}
Calling this code from an action handler within a form yields the following error Method expects to have requestAsyncStorage, none available. Is there anyone who has successfully gotten this to work?
21 Replies
Browassup
Browassup2y ago
are u doing this in the api folder?
rykuno
rykunoOP2y ago
This is a server function in a file actions.ts co-located along pages.tsx
Browassup
Browassup2y ago
well when im doing something like this i usually write this code in the api folder and send the form data from any component using axios. Ive done it many time in the app dir. Ive not really gone deep into using the "use server" , and server functions
Samathingamajig
is this on the edge runtime? or locally/lambda
rykuno
rykunoOP2y ago
this is locally no edge runtime Okay hold the fuck up. If I call a server function from a server component I can access getServerSession. As follows
import { createEvent } from "./_actions";
import EventCreateForm from "./form";

export default function EventCreate() {
return (
<div className="">
<EventCreateForm createEvent={createEvent} />
</div>
);
}
import { createEvent } from "./_actions";
import EventCreateForm from "./form";

export default function EventCreate() {
return (
<div className="">
<EventCreateForm createEvent={createEvent} />
</div>
);
}
But If i call the function from a form action, it errors out. Shouldn't both be called on the server side thus have the same access to the cookies????
rykuno
rykunoOP2y ago
Samathingamajig
maybe nextauth is not ready for app directory?
rykuno
rykunoOP2y ago
Its actually not a NextAuth issue I found out. The error was w/ accessing the browsers storage so I tried to access cookies just for fun
export async function createEvent(formData: FormData) {
"use server";
cookies().getAll();
}
export async function createEvent(formData: FormData) {
"use server";
cookies().getAll();
}
It gave the exact same error
Samathingamajig
well you can't access any browser storage with code that runs on the server (without passing it in as an argument)
rykuno
rykunoOP2y ago
Data Fetching: Server Actions
Use Server Actions to mutate data in your Next.js application.
rykuno
rykunoOP2y ago
This snippet when called from a form action within a client component will not have access to cookies; which seems wrong? Its called on the client, but the action itself is a server action. Thus it shouldn't matter if a server or client component calls it so long as its done in the appropriate manner
Samathingamajig
check the request in the dev tools of your browser, see if the cookie is sent
rykuno
rykunoOP2y ago
yep in both cases the cookies are sent. Yet errors out when the server function is called client side via action
rykuno
rykunoOP2y ago
I'm not the first to discover it apparently https://github.com/vercel/next.js/issues/49235
GitHub
Importing server action from 'use client' component results in erro...
Verify canary release I verified that the issue exists in the latest Next.js canary release Provide environment information Operating System: Platform: linux Arch: x64 Version: #202204271406~165547...
Samathingamajig
oh you're using an action in a client component, i don't know if you're allowed to do that
rykuno
rykunoOP2y ago
According to their docs it should be 100% allowed.
rykuno
rykunoOP2y ago
Server Actions cannot be defined within Client Components, but they can be imported. To use Server Actions in Client Components, you can import the action from a file containing a top-level "use server" directive.
Server Actions cannot be defined within Client Components, but they can be imported. To use Server Actions in Client Components, you can import the action from a file containing a top-level "use server" directive.
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#examples
Data Fetching: Server Actions
Use Server Actions to mutate data in your Next.js application.
Samathingamajig
action as in action={...} you're definitely allowed to call them as function but i don't know if you're allowed to have them on a <form> tag's action attribute ok looks like you can
rykuno
rykunoOP2y ago
well the temporary work around is to just call it straight from a server component I guess. Im on 13.4.1 so I'll keep my eye out for a fix I suppose.
Browassup
Browassup2y ago
have u found the solution?
kevin8426
kevin84262y ago
Hey Guys, I found this thread through search and thought I'd chime in: There is currently no official fix for this but there is a workaround for the time being: Wrap your client component in a server component. Import the server function in a server component and pass it as a prop to the client component and call it there. That works but is more boilerplate than I would like. Like this:
// action.ts
'use server';
export async function serverFn(){...};

// ClientComponent.tsx
'use client'
import { serverFn } from './action'; // We use this only for the typing
type Props = {func: typeof serverFn};
export function ClientComponent({func}: Props){
const [count, setCount] = React.useState(0)
// Use function here however you like
return <button onClick={func}>Call Server Action {count}</button>
}

// Wrapper.tsx
import { serverFn } from './action';
import { ClientComponent } from './ClientComponent'
export default Wrapper(){
return <ClientComponent func={serverFn} />
}
// action.ts
'use server';
export async function serverFn(){...};

// ClientComponent.tsx
'use client'
import { serverFn } from './action'; // We use this only for the typing
type Props = {func: typeof serverFn};
export function ClientComponent({func}: Props){
const [count, setCount] = React.useState(0)
// Use function here however you like
return <button onClick={func}>Call Server Action {count}</button>
}

// Wrapper.tsx
import { serverFn } from './action';
import { ClientComponent } from './ClientComponent'
export default Wrapper(){
return <ClientComponent func={serverFn} />
}
Now using cookies or headers from next/headers works fine.
Want results from more Discord servers?
Add your server