#Proplems with server actions

1 messages · Page 1 of 1 (latest)

exotic trail
#

I am pretty new with next Js. But. I have an app where i whant to give to every client a diferent id. To do that i have a server action that gets the client id and if it dosen't exist it's going to create it. I am using cookies-next.

export async function getClientId() {
    const id = await getCookie("client-id");
    if (!id) {
        const newId = crypto.randomUUID();
        setCookie("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
        return newId;
    }
    return id;
}

The problem is that something is wrong because the cookie dosen't get saved on the client if i add

<Button variant="link" onClick={getClientId} className="mt-4">Obține Client ID</Button>

to a file that is marked with "use server" it does not work. It generates a new cookie on every click.

north girderBOT
#

🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord

🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize

✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)

warped pike
#

i think you need to await setCookie()

exotic trail
#

They are from the library cookies-next

exotic trail
warped pike
#

try without using cookie-next

exotic trail
#

The thing is if i specify to use cookies-next/server next js complains about client using server functions

exotic trail
#

And yes i tried to use next headers

#

I got the error that i use server functions on the client

warped pike
#

you're overcomplicating the issue.

it works with just (await cookies()).set(...)

#

its not about server actions but about something else

#

probably about your usage of cookies-next/server

exotic trail
#

I tested (await cookies()).set() and .get and i think it didn't work i am able to test this in 20 min

#

The thing is that in the same file i use fs so it has to be the server. And when i call the server the server action gets called

#

When i call the function

warped pike
#

allright, please send complete minimal reproducible code because at this point im confused whether or not you've set up server action correctly or not

#

i.e where you put "use server", and where you put "use client"

exotic trail
#

Ok. I can provide code in 20 min

warped pike
#

"in the same file" like which file? The file of <Button> or the file of getClientId?

exotic trail
#

In the same file the getClientId is declared i have another function that uses fs

#

so i cropped the file from all the functions i have inside it but:

"use server";

import fs, { access, constants } from "node:fs/promises";
import { getCookie, setCookie } from "cookies-next";
import { v4 as uuidv4 } from "uuid";
import path from "node:path";

export async function getClientId() {
    const id = await getCookie("client-id");
    if (!id) {
        console.warn("Client ID este hardcodat.")
        const newId = "f8f16d33-62a1-4852-8386-7bb5fbbb55c2"; //crypto.randomUUID();
        await setCookie("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
        return newId;
    }
    return id;
}
#

and in a page.tsx i have ```ts

import UploadForm from "../../components/UploadForm";
import { getClientId, listUploadedFiles } from "./server";
import { Button } from "@/components/ui/button";

export default async function Home() {
const files = await listUploadedFiles();

return (
    <main>
        <div className="flex flex-wrap justify-center m-8">
            <UploadForm />
            <Button variant="link" onClick={getClientId} className="mt-4">Obține Client ID</Button>
        </div>
    </main>
);

}```

warped pike
#

thanks for sending me minimal code.

#

can we also have the error log and whats inside of <Button> ?

exotic trail
#

no errors. and in the DOM ?

#

in the file it has text

warped pike
#

no errors so it works?

exotic trail
#

no sorry i added that hardcoded id because it didn't work and i needed to test other parts of the app

exotic trail
#

POST /uploads 200 in 54ms (compile: 25ms, render: 29ms)
Client ID este hardcodat.
GET /upload 200 in 727ms (compile: 413ms, render: 314ms)

warped pike
#

lets just try making the code more minimal and troubleshoot this

#

try using <form action={getClientId}> instead of <Button>

exotic trail
#

ok. should i wrap the button with that from and make the button type submit?

warped pike
#

no just do simple <form action={getClientId}><button>Submit</button></form>

exotic trail
#

<form className="ml-4" action={() => { getClientId() }}>
<button>Obține Client ID</button>
</form>. the function returns something and ts isn't happy about it.

warped pike
#

have you tried the exact code i wrote?

exotic trail
#

yea and it still makes a new id on each call

#
        <main>
            <div className="flex flex-wrap justify-center m-8">
                <UploadForm />
                <form action={getClientId}>
                    <button>Obține Client ID</button>
                </form>
            </div>
        </main>
warped pike
#

have you tried without using cookies-next ?

exotic trail
#

i

#

i'l try

warped pike
#

thank you

exotic trail
#

⨯ Error: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options
at getClientId (app\upload\server.tsx:93:21)
at async listUploadedFiles (app\upload\server.tsx:78:22)
at async Home (app\upload\page.tsx:7:19)
91 | const newId = crypto.randomUUID(); //"f8f16d33-62a1-4852-8386-7bb5fbbb55c2"; //
92 | console.warn("Client ID este este:", newId)

93 | cookieStore.set("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
| ^
94 | return newId;
95 | }
96 | return id.value; {
digest: '1433263048'
}
GET /upload 200 in 517ms (compile: 7ms, render: 511ms)

warped pike
#

that shows that you've used getClientId somewhere else

#

not in Button :(

#

in here const files = await listUploadedFiles();

exotic trail
#

that is in the same file. on top the the getclientId declaration

#
export async function listUploadedFiles() {
    const clientId = await getClientId();
    const dirPath = `${process.env.FILESSTORAGEPATH}/${clientId}`;
    if (!await fileExists(dirPath)) {
        return [];
    }
    const files = await fs.readdir(dirPath);
    return files;
}

export async function getClientId() {
    const cookieStore = await cookies();
    const id = cookieStore.get("client-id");
    if (!id) {
        const newId = crypto.randomUUID(); //"f8f16d33-62a1-4852-8386-7bb5fbbb55c2"; //
        console.warn("Client ID este este:", newId)
        cookieStore.set("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
        return newId;
    }
    return id.value;
}
warped pike
#

yes so you can't setCookies in server components

#

your getClientId is in listUploadedFiles

#

your listUploadedFiles is in Home()

#

Home() is a server component

#

can't set cookie in server components

exotic trail
#

so what do i need to do ?

warped pike
#

dont set cookie in getClientId

exotic trail
#

were should i set it?

warped pike
#

use ClientComponent that runs on useEffect, and call ensureClientIdExistAction() that uses "use server" and make sure "client-id" cookie exists. if not then cookieStore.set or setCookies()

#

put ClientComponent at root layout so that it doesn't re-render on navigation

exotic trail
#

so. I should make a new function in the use server file that makes the cookie and to call it with an use effect in the layout page like this:
the layout page ```ts
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {

useEffect(() => {
ensureClientIdExistAction();
}, []);

return (
<html lang="en">
<body
className={${geistSans.variable} ${geistMono.variable} antialiased}
>
{children}
</body>
</html>
);
}

and 
```ts
export async function ensureClientIdExistAction() {
    const newId = uuidv4();
    const cookieStore = await cookies();
    cookieStore.set("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
}
``` do i need to make the layout page client
warped pike
#

create Client Component, put "use client" on top, put Client Component on RootLayout.

#

and dont make the Action like that. Use the same code that you write like if(!id){ ... }

exotic trail
#

so ```ts
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {

return (
<html lang="en">
<body
className={${geistSans.variable} ${geistMono.variable} antialiased}
>
<EnsureCookie />
{children}
</body>
</html>
);
}

with ```ts
"use client"

import { ensureClientIdExistAction } from "@/app/upload/server";
import { useEffect } from "react";

function EnsureCookie() {
  useEffect(() => {
    ensureClientIdExistAction();
  }, []);

  return (
    <div />
  )
}

export default EnsureCookie
warped pike
#

yeah

#

something like that

#

just return <></>

exotic trail
warped pike
#

its fine

exotic trail
#
export async function ensureClientIdExistAction() {
    const cookieStore = await cookies();
    if (cookieStore.get("client-id")) {
        return;
    }
    const newId = uuidv4();
    cookieStore.set("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
}
north girderBOT
warped pike
#

awesome glad it works!

#

now you can proceed in using cookies-next for your convenience

exotic trail
#

tho. i don't know if i can do this

export async function getClientId() {
    const cookieStore = await cookies();
    const id = cookieStore.get("client-id");
    if (!id) {
        throw new Error("Client ID nu există în cookie-uri.");
    }
    return id.value;
}
#

as it doesn't seem to get called before the other components

#

and i replaced it with ```ts
export async function getClientId() {
const cookieStore = await cookies();
const id = cookieStore.get("client-id");
if (!id) {
//throw new Error("Client ID nu există în cookie-uri.");
return "";
}
return id.value;
}

#

as this fixed it but i wished it didn't it dosen't seem to be a clean sollution

warped pike
#

you can try doing something like

export async function getClientId() {
    const cookieStore = await cookies();
    const id = cookieStore.get("client-id");
    if (!id) {
        const newId = crypto.randomUUID();
        console.warn("Client ID este este:", newId)
        try {
          cookieStore.set("client-id", newId, { maxAge: 60 * 60 * 24 * 365 * 20 });
        } catch { }
        return newId;
    }
    return id.value;
}

if you want cleaner solution