Is it currently possible to use clerk auth with NextJS' 13.4 server actions?

I know it's currently in alpha, but I'd like to know if there is any way to use it right now. Library versions are the latest.
@acme/nextjs:dev: - error Error: Clerk: auth() and currentUser() are only supported in App Router (/app directory).
@acme/nextjs:dev: If you're using /pages, try getAuth() instead.
@acme/nextjs:dev: Original error: Error: Invariant: Method expects to have requestAsyncStorage, none available
@acme/nextjs:dev: at handleUpdateNoteContents (./src/app/@modal/(.)e/[id]/actions.ts:13:82)
@acme/nextjs:dev: - error Error: Clerk: auth() and currentUser() are only supported in App Router (/app directory).
@acme/nextjs:dev: If you're using /pages, try getAuth() instead.
@acme/nextjs:dev: Original error: Error: Invariant: Method expects to have requestAsyncStorage, none available
@acme/nextjs:dev: at handleUpdateNoteContents (./src/app/@modal/(.)e/[id]/actions.ts:13:82)
// actions.ts
"use server";

import { currentUser } from "@clerk/nextjs";

import { db } from "@acme/db";

export const handleUpdateNoteContents = async (id: string, content: string) => {
const user = await currentUser();
if (!user) {
return {
error: "You must be logged in to update a note",
};
}
const { id: userId } = user;
await db
.updateTable("Note")
.set({
content,
})
.where(({ and, cmpr }) =>
and([cmpr("id", "=", id), cmpr("userId", "=", userId)]),
)
.executeTakeFirstOrThrow();
};
// actions.ts
"use server";

import { currentUser } from "@clerk/nextjs";

import { db } from "@acme/db";

export const handleUpdateNoteContents = async (id: string, content: string) => {
const user = await currentUser();
if (!user) {
return {
error: "You must be logged in to update a note",
};
}
const { id: userId } = user;
await db
.updateTable("Note")
.set({
content,
})
.where(({ and, cmpr }) =>
and([cmpr("id", "=", id), cmpr("userId", "=", userId)]),
)
.executeTakeFirstOrThrow();
};
// page.tsx
'use client'
// ...
export default function EditNoteModal({ params }: { params: { id?: string } }) {
const [isPending, startTransition] = useTransition();
// ...
const router = useRouter();
// ..
const updateContent = ({ content }: EditFormData) => {
const add = async () =>
startTransition(() => {
handleUpdateNoteContents(params.id!, content);
});

reset();
add()
.then(() => {
router.refresh();
router.back();
})
.catch((err) => console.error(err));
};
//...
}
// page.tsx
'use client'
// ...
export default function EditNoteModal({ params }: { params: { id?: string } }) {
const [isPending, startTransition] = useTransition();
// ...
const router = useRouter();
// ..
const updateContent = ({ content }: EditFormData) => {
const add = async () =>
startTransition(() => {
handleUpdateNoteContents(params.id!, content);
});

reset();
add()
.then(() => {
router.refresh();
router.back();
})
.catch((err) => console.error(err));
};
//...
}
11 Replies
inselbegabter
inselbegabterOP•2y ago
I feel like @James Perkins knows a lot about clerk 😄
James Perkins
James Perkins•2y ago
I know too much about Clerk.
inselbegabter
inselbegabterOP•2y ago
hahahaha
James Perkins
James Perkins•2y ago
So there is a bug in Next.js that causes this problem. 🙂 Which hasn't been fixed by them yet, we reported it like 2 months ago or so. But it can still be used, just gotta dig it up
inselbegabter
inselbegabterOP•2y ago
Thats good to know! I was going crazy already. Thank you for your reply 🙂 Really? I'd love to know how 😄
James Perkins
James Perkins•2y ago
So I think I remember correctly. you need to import auth() on the page that the action is also happening. So for example, this should work as a server action. even though it's all on one page:
import { auth, currentUser } from '@clerk/nextjs';

export default function AddToCart() {
async function addItem(data: any) {
'use server';
console.log(auth().userId);
console.log((await currentUser())?.firstName);
console.log('add item server action', data);
}

return (
// @ts-ignore
<form action={addItem}>
<input
value={'test'}
type='text'
name='name'
/>
<button type='submit'>Add to Cart</button>
</form>
);
}
import { auth, currentUser } from '@clerk/nextjs';

export default function AddToCart() {
async function addItem(data: any) {
'use server';
console.log(auth().userId);
console.log((await currentUser())?.firstName);
console.log('add item server action', data);
}

return (
// @ts-ignore
<form action={addItem}>
<input
value={'test'}
type='text'
name='name'
/>
<button type='submit'>Add to Cart</button>
</form>
);
}
inselbegabter
inselbegabterOP•2y ago
I'll try it and let you know it it works. Thanks James!
James Perkins
James Perkins•2y ago
Yeah, it may or may not work it's very much a early early alpha a lot of usage seems to break even though it should work
inselbegabter
inselbegabterOP•2y ago
When adding your server actions to a actions.ts file, it doesn't work. I like to use this pattern, bc it allows me to use the server actions in a client component. When using clerk in a pure server component, I don't need to use auth() to get it working. I know it's still in alpha. Can't wait to use everything properly once it gets released
saM69420
saM69420•2y ago
I ran into this issue with my client component and the solution was to import the server action into the parent server component and pass it down to the client component.
When using client components you need to make sure you use prop drilling to ensure that headers are available.
https://clerk.com/docs/nextjs/server-actions#with-client-components
Server Actions | Clerk
Learn how to use Clerk with Next.js Server Actions
James Perkins
James Perkins•2y ago
Yup forgot to circle back. We release docs to explain how to use it today.
Want results from more Discord servers?
Add your server