Next.js Discord

Discord Forum

I cant modify the cookies in a server action of axios

Unanswered
Black Caiman posted this in #help-forum
Open in Discord
Black CaimanOP
Hello Everyone

I have been trying to refresh the authentication token in my Next.js 14 project based on the response from an external API. So far, I can send the requests correctly using cookies().get(name) from next/headers to access the cookies. However, when I attempt to refresh or modify the cookies from a file that is a Server Action, Next.js tells me that cookies can only be modified in a Server Action or a Route Handler.

The error is the following:
Error refreshing token: tA [Error]: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options at Proxy.callable (C:\Route_to_the_proyect\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:36:12959) at setCookies (webpack-internal:///(rsc)/./src/app/actions/set-cookies.ts:15:23) at refreshToken (webpack-internal:///(rsc)/./src/app/actions/httpAxios2.ts:40:75) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async handleError (webpack-internal:///(rsc)/./src/app/actions/httpAxios2.ts:70:35) at async makeRequest (webpack-internal:///(rsc)/./src/app/actions/httpAxios2.ts:111:29) at async getUserProfile (webpack-internal:///(rsc)/./src/app/dashboard/(framed)/profile/infrastructure/profileRepository.ts:14:40) at async getUserInfo (webpack-internal:///(rsc)/./src/app/hooks/getUserInfo.ts:18:18) at async Layout (webpack-internal:///(rsc)/./src/app/dashboard/(framed)/layout.tsx:23:18)


The curious thing is that my file is indeed a Server Action, but the cookies are not updating. I also tried making these modifications from a Route Handler, but they do not set, change, or get deleted, and I can’t even access them via cookies().getAll(). My code is the following:

26 Replies

@Black Caiman Hello Everyone I have been trying to refresh the authentication token in my Next.js 14 project based on the response from an external API. So far, I can send the requests correctly using cookies().get(name) from next/headers to access the cookies. However, when I attempt to refresh or modify the cookies from a file that is a Server Action, Next.js tells me that cookies can only be modified in a Server Action or a Route Handler. The error is the following: Error refreshing token: tA [Error]: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options at Proxy.callable (C:\Route_to_the_proyect\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:36:12959) at setCookies (webpack-internal:///(rsc)/./src/app/actions/set-cookies.ts:15:23) at refreshToken (webpack-internal:///(rsc)/./src/app/actions/httpAxios2.ts:40:75) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async handleError (webpack-internal:///(rsc)/./src/app/actions/httpAxios2.ts:70:35) at async makeRequest (webpack-internal:///(rsc)/./src/app/actions/httpAxios2.ts:111:29) at async getUserProfile (webpack-internal:///(rsc)/./src/app/dashboard/(framed)/profile/infrastructure/profileRepository.ts:14:40) at async getUserInfo (webpack-internal:///(rsc)/./src/app/hooks/getUserInfo.ts:18:18) at async Layout (webpack-internal:///(rsc)/./src/app/dashboard/(framed)/layout.tsx:23:18) The curious thing is that my file is indeed a Server Action, but the cookies are not updating. I also tried making these modifications from a Route Handler, but they do not set, change, or get deleted, and I can’t even access them via cookies().getAll(). My code is the following:
Double-striped Thick-knee
1. are you aware that when you write "use server" at the top of a file, all the exported functions from that file becomes server actions and can be accessed from client.

2. what does setCookies function does?
Black CaimanOP
"use server";

import { cookies } from "next/headers";

export default async function setCookies(setCookieHeader: string[]) {
setCookieHeader.filter(cookie => cookie).forEach((cookie) => {
const [cookieRaw, ..._] = cookie.split("; ");
const [cookieName, ...cookieAttributes] = cookieRaw.split("=");
cookies().set(cookieName, cookieAttributes.join("="), {
httpOnly: true,
sameSite: "lax"
});
});
}
This is the setCookies function
@Double-striped Thick-knee 1. are you aware that when you write "use server" at the top of a file, all the exported functions from that file becomes server actions and can be accessed from client. 2. what does setCookies function does?
Black CaimanOP
Yes, im trying to get through the error making all the request from server actions, but nothing works and it always give me the same error
Double-striped Thick-knee
which action are you calling to refresh token, there are so many in your provided code, so it's hard for me to keep track.
Black CaimanOP
'use server'
import { cookies } from "next/headers";

export default async function getCookies(name: string) {
    const cookie = cookies();
    return cookie.get(name)
}


And this is the getCookies function, the weird thing is that this function works perfectly fine cause it doesn't mody the cookies
@Double-striped Thick-knee which action are you calling to refresh token, there are so many in your provided code, so it's hard for me to keep track.
Black CaimanOP
Im so sorry, the function to refresh the token is the first one below the customHeaders
@Black Caiman Im so sorry, the function to refresh the token is the first one below the customHeaders
Double-striped Thick-knee
you haven't exported that function, so how are you calling it from the client?
@Double-striped Thick-knee you haven't exported that function, so how are you calling it from the client?
Black CaimanOP
you see in the function makeRequest if the request fails, i call the refresh function there, in the text i put i might got deleted here is the updated makeRequest function

// Función genérica para hacer peticiones HTTP
export async function makeRequest<T>(
  method: 'get' | 'post' | 'put' | 'patch' | 'delete',
  path: string,
  params?: any,
  config: AxiosRequestConfig = {}
): Promise<T> {
  const url = `${BASE_URL}${path}`;
  const configWithHeaders = await configureHeaders(config);

  try {
    let response;
    switch (method) {
      case 'get':
        response = await axios.get(url, { ...configWithHeaders, params });
        break;
      case 'post':
        response = await axios.post(url, params, configWithHeaders);
        break;
      case 'put':
        response = await axios.put(url, params, configWithHeaders);
        break;
      case 'patch':
        response = await axios.patch(url, params, configWithHeaders);
        break;
      case 'delete':
        response = await axios.delete(url, { ...configWithHeaders, params });
        break;
    }
    return response.data;
  } catch (error) {
    if (axios.isAxiosError(error) && error.response?.status === 403) {
      const refreshSuccessful = await refreshToken();
      if (refreshSuccessful) {
        return makeRequest(method, path, params, config);
      } else {
        await removeCookie("accessToken");
        await removeCookie("refreshToken");
        throw new Error("Authentication failed");
      }
    }
    throw error;
  }
}
@Double-striped Thick-knee have you checked if refreshToken function is getting called?
Black CaimanOP
Yes, it makes the request and console log me the tokens in the set-cookie header, but when i try to use the setCookie function, there is where the error take place
@Black Caiman Yes, it makes the request and console log me the tokens in the set-cookie header, but when i try to use the setCookie function, there is where the error take place
Double-striped Thick-knee
show your clients code from where you're calling makeRequest function.
Black CaimanOP
well first i use it in this request which are in the same file of the makeRequest:

export async function get<T>(path: string, params?: Record<string, any>, config?: AxiosRequestConfig) {
  return makeRequest<T>('get', path, params, config);
}

export async function post<T>(path: string, params?: Record<string, any> | FormData, config?: AxiosRequestConfig) {
  return makeRequest<T>('post', path, params, config);
}

export async function put<T>(path: string, params?: Record<string, any> | FormData, config?: AxiosRequestConfig) {
  return makeRequest<T>('put', path, params, config);
}

export async function patch<T>(path: string, params?: Record<string, any> | FormData, config?: AxiosRequestConfig) {
  return makeRequest<T>('patch', path, params, config);
}

export async function del<T>(path: string, params?: any, config?: AxiosRequestConfig) {
  return makeRequest<T>('delete', path, params, config);
}
And then i call it in a component:
import { get } from "@/http/infrastructure/httpAxios2";
import { Card, NavigationByRole } from "@/app/_shared";
import { auth } from "root/auth";
import { Roles } from "@/domain";
import { redirect } from "next/navigation";

export default async function Home() {
  const session = await auth();

  const { UserTypeId, name,hasToChangePassword } = await get<Profile>(`/user/profile/${session?.user.id}`);

  const IsUserWhoChangePwd: boolean =
    UserTypeId === Roles.user || UserTypeId === Roles.client;

  if (hasToChangePassword && IsUserWhoChangePwd)
    redirect("/dashboard/change-password");

  return (
    <main className="w-full h-full py-5 sm:px-10">
      <section className="text-start pt-5">
        <h1 className="text-5xl font-normal text-primary">
          ¡Hola, <span className="text-black">{name}</span>! 👋
        </h1>
        <p className="text-md font-light mt-5 max-w-[25rem]">
          Comunica y coordina encuentros con expertos en salud fácilmente
        </p>
      </section>
      <section className="mt-8 w-full">
        <h2 className="text-2xl font-semibold py-2 text-neutral-600 pl-2">
          Acciónes
        </h2>
        <div className="grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-10 lg:w-[540px]">
          {NavigationByRole.map((item, index) => {
            if (item.roles.includes(UserTypeId) && item.nameOption !== "Inicio")
              return (
                <Card
                  key={index}
                  to={item.redirect}
                  imgComponent={
                    <item.icon className="w-6 h-6 text-[#ae7a6c]" />
                  }
                  title={item.nameOption}
                />
              );
          })}
        </div>
      </section>
    </main>
  );
}
Double-striped Thick-knee
@Black Caiman btw maybe I should've told it before but you cannot call a server action from server components if that server action tries to set some cookies.
Black CaimanOP
yes, that is a component example but i call it in other part
Quick question, ¿can i call a server action from another server action?
Double-striped Thick-knee
you can, they will be treated as regular functions
Black CaimanOP
cause the thing is that the get im doing there, in reality im doing it on a server actions and then call it in that component, and that worked completly fine
Double-striped Thick-knee
when you call a server action from a server component, that server action will be treated as a regular function. so if you set cookies from there, it will throw error since server components can only read cookies, cannot set them
here is an example
"use server";
import { cookies } from "next/headers";

export async function serverAction() {
    cookies().set("key", "value");
}


since serverAction function is trying to setCookie, calling it from this component throws error,
import { serverAction } from "./actions";

// server component
export default async function page() {
    await serverAction()
    return <div>Server Component</div>
}
calling it from client component doesn't throw
"use client";
import { serverAction } from "./actions";

export default function ClientComponent() {
    return <div onClick={() => serverAction()}>page</div>;
}
@Black Caiman here's the error I got when directly called it from server component
Black CaimanOP
oh, i see, so what can i do?
@Black Caiman oh, i see, so what can i do?
Double-striped Thick-knee
you can trying refreshing token from client component
Black CaimanOP
thank you so much, i'm going to try it out