#Get cookies in Next.js

1 messages · Page 1 of 1 (latest)

glossy pecan
#

Get the cookie from Server Actions or router, which one is the right way?
And explain the reason, compare the differences of usage too.

sleek karmaBOT
#

Hi @glossy pecan. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

In Next.js, you can get cookies both in Server Actions and Route Handlers. The choice between the two depends on your specific use case.

Server Actions:
In Server Actions, you can get, set, and delete cookies using the cookies API. This is useful when you need to manipulate cookies as part of server-side data fetching or mutations. Here's an example of how to get a cookie in a Server Action:

'use server'
 
import { cookies } from 'next/headers'
 
export async function exampleAction() {
  // Get cookie
  const value = cookies().get('name')?.value
}

(Source: Next.js Docs - Server Actions and Mutations (https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#cookies))

Route Handlers:
In Route Handlers, you can also read or set cookies using the cookies function from next/headers. This is useful when you need to manipulate cookies as part of routing logic. Here's an example of how to get a cookie in a Route Handler:

import { cookies } from 'next/headers'
 
export async function GET(request: Request) {
  const cookieStore = cookies()
  const token = cookieStore.get('token')
}

(Source: Next.js Docs - Route Handlers (https://nextjs.org/docs/app/building-your-application/routing/route-handlers#dynamic-functions))

Differences:
The main difference between the two is the context in which they are used. Server Actions are typically used for server-side data fetching and mutations, while Route Handlers are used for routing logic. If you need to manipulate cookies as part of data fetching or mutations, use Server Actions. If you need to manipulate cookies as part of routing, use Route Handlers.

#

Remember, HTTP does not allow setting cookies after streaming starts, so you cannot set cookies from a page or layout directly. However, you can also set cookies from Middleware in Next.js.

(Source: Next.js Docs - How can I set cookies? (https://nextjs.org/docs/app#how-can-i-set-cookies))

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@glossy pecan kapa.ai is still learning and improving, please let us know how it did by reacting below

glossy pecan
#

@sleek karma


import { getCookie } from "@/lib/authCookie";
import { axiosInstance } from "@/lib/axiosInstance";
import { cookies } from "next/headers";

export const resendConfirmationEmail = async () => {
  try {

    const jwtToken = cookies().get('auth')?.value
    // console.log('JWT Token:', jwtToken);

    if (!jwtToken) {
      console.error('JWT token not found in cookies.');
      return false;
    }

    const response = await axiosInstance.post('/api/auth/local/resend-confirmation',
      {},
      {
        headers: {
          Authorization: `Bearer ${jwtToken}`,
        },
        withCredentials: true, // HTTP-Only Cookie
      }
    );

    return response.status === 200;
  } catch (error) {
    console.error('Error resending email:', error);
    return false;
  }
};

Those are my server action code. What's the difference using export async function exampleAction() between mine?
Also, I can't get cookies with this code, Probably I should use make it function that you gave me?

sleek karmaBOT
#

Let me check the knowledge base for you. One moment! :books:

#

The main difference between your code and the export async function exampleAction() example is the way the function is declared and exported. In the example, the function is declared using the function keyword and then exported, while in your code, you're using an arrow function and exporting it directly.

Here's the example for comparison:

'use server'
 
import { cookies } from 'next/headers'
 
export async function exampleAction() {
  // Get cookie
  const value = cookies().get('name')?.value
}

(Source: Next.js Docs - Server Actions and Mutations (https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#cookies))

In terms of functionality, both ways of declaring functions are equivalent. However, the function keyword allows the function to be hoisted, meaning it can be used before it's declared. Arrow functions, on the other hand, are not hoisted.

As for getting cookies, you should be able to get cookies in your server action using the cookies function from next/headers, just like in the example. If you're not able to get cookies, it could be due to a number of reasons, such as the cookie not being set correctly, the cookie being HttpOnly (and thus not accessible through JavaScript), or the cookie's SameSite attribute preventing it from being sent in a cross-site context.

If you're still having trouble, you might want to try using the function keyword to declare your function, like in the example, to see if that makes a difference.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@glossy pecan kapa.ai is still learning and improving, please let us know how it did by reacting below

glossy pecan
#

@sleek karma What if I got 401 error from those code? What's the problem?

sleek karmaBOT
#

Let me check the knowledge base for you. One moment! :books:

#

A 401 error typically indicates that authentication has failed. This could be due to an invalid or missing API token. In the context of your code, it could mean that the jwtToken you're using to authenticate your request to the /api/auth/local/resend-confirmation endpoint is invalid or not present.

Here's the relevant part of your code:

const jwtToken = cookies().get('auth')?.value

// ...

const response = await axiosInstance.post('/api/auth/local/resend-confirmation',
  {},
  {
    headers: {
      Authorization: `Bearer ${jwtToken}`,
    },
    withCredentials: true, // HTTP-Only Cookie
  }
);

If jwtToken is undefined, the Authorization header will be set to Bearer undefined, which would cause the server to return a 401 error.

You should ensure that the auth cookie is being set correctly and that it contains a valid JWT token. If the cookie is not being set, you'll need to investigate why that's the case. If the cookie is being set but the token is invalid, you'll need to look into how and where the token is being generated.

(Source: Vercel KV Rest API - Errors (https://vercel.com/docs/storage/vercel-kv/rest-api#errors))

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

glossy pecan
#

@sleek karma
'NEXT_REDIRECT;replace;/confirmation/message;303;' Error
This is my code, it kept showing me this error after my register. how can I resolve it?

"use server";

const config = {
  maxAge: 60 * 60 * 24 * 7, // 1 week
  path: '/',
  sameSite: 'none' as 'none',
  httpOnly: false,
  secure: true,
}

export default async function signUpAction(
  prevState: SignUpFormStateT,
  formData: FormData
): Promise<SignUpFormStateT | undefined> {
  // Validate form data using zod schema
  const validatedFields = formSchema.safeParse({
    username: formData.get('username'),
    email: formData.get('email'),
    password: formData.get('password'),
  });

  if (!validatedFields.success) {
    return {
      error: true,
      inputErrors: validatedFields.error.flatten().fieldErrors,
      message: 'Please verify your data',
    };
  }

  const { username, email, password } = validatedFields.data;

  try {
    console.log('Request URL:', `${axiosInstance.defaults.baseURL}/api/auth/local/registers`);

    // Send sign-up request to Strapi API
    const response = await axiosInstance.post(
      '/api/auth/local/registers',
      {
        username, email, password
      });

    console.log('Strapi Response Status:', response.status);
    console.log('Strapi Response Data:', response.data);
    console.log('Token:', response.data.token);

    if (response.status === 200) {
      const authToken = response.data.token;
      cookies().set('auth', authToken, config);
      
      redirect('/confirmation/message');
      return;
    }
    
    return { error: true, message: 'An unknown error occurred.'}
  } catch (error) {
    console.error('Axios Error:', error);
    return handleAxiosError(error as AxiosError);
  }
};

This is the code for the ServerAction.

sleek karmaBOT
#

Let me check the knowledge base for you. One moment! :books:

#

The error message NEXT_REDIRECT;replace;/confirmation/message;303; is thrown when you try to use the redirect function in a server action. This is because Next.js does not currently support redirects from server actions.

In your code, you're trying to redirect to /confirmation/message after a successful sign-up:

if (response.status === 200) {
  const authToken = response.data.token;
  cookies().set('auth', authToken, config);
  
  redirect('/confirmation/message');
  return;
}

To work around this limitation, you can return a response from your server action that indicates a redirect is needed, and then handle the redirect on the client side.

For example, you could return a response like this:

if (response.status === 200) {
  const authToken = response.data.token;
  cookies().set('auth', authToken, config);
  
  return { redirect: '/confirmation/message' };
}

Then, on the client side, you can check for this redirect property in the response and use router.push to perform the redirect:

const response = await signUpAction(prevState, formData);
if (response.redirect) {
  router.push(response.redirect);
}

This way, you're still able to perform the redirect, but you're doing it on the client side where it's supported.

(Source: Github Issue (closed): Redirect inside server action throws an error (https://github.com/vercel/next.js/issues/58002))

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@glossy pecan kapa.ai is still learning and improving, please let us know how it did by reacting below

glossy pecan
#

@sleek karma
The thing is I'd like to redirect in ServerAction. Is it impossible to do that?

sleek karmaBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Yes, you can redirect in a Server Action in Next.js. However, the redirect function should be called outside of the try/catch block. This is because the redirect function internally throws an error to halt the execution of the current function and start the redirection process. If it's called inside a try/catch block, the redirection error will be caught and treated as an exception, and the redirection won't occur.

Here's an example of how to use the redirect function in a Server Action:

'use server'
 
import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
 
export async function createPost(id: string) {
  try {
    // ...
  } catch (error) {
    // ...
  }
 
  revalidateTag('posts') // Update cached posts
  redirect(`/post/${id}`) // Navigate to the new post page
}

(Source: Next.js Docs - Server Actions and Mutations (https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#redirecting))

In your case, you can move the redirect call outside of the try/catch block like this:

'use server'

// ...

export default async function signUpAction(
  prevState: SignUpFormStateT,
  formData: FormData
): Promise<SignUpFormStateT | undefined> {
  // ...

  try {
    // ...

    if (response.status === 200) {
      const authToken = response.data.token;
      cookies().set('auth', authToken, config);
    }
    
  } catch (error) {
    // ...
  }

  if (response && response.status === 200) {
    redirect('/confirmation/message');
  } else {
    return { error: true, message: 'An unknown error occurred.'}
  }
};

Please note that when used in a server action, redirect will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 307 HTTP redirect response to the caller.

#

@glossy pecan kapa.ai is still learning and improving, please let us know how it did by reacting below

glossy pecan
#

@sleek karma Don't forget that I'm using the typeScript. I can't

if (response && response.status === 200) {
    redirect('/confirmation/message');
  } else {
    return { error: true, message: 'An unknown error occurred.'}
  }

Build the code like that.

export type SignUpFormStateT = {
  error: boolean;
  message: string;
  inputErrors?: InputErrorsT;
};

This is the type that I made.

sleek karmaBOT
#

Let me check the knowledge base for you. One moment! :books:

#

I see, you're using TypeScript and your function is expected to return a value of type SignUpFormStateT or undefined. The redirect function in Next.js doesn't return a value, it throws a NEXT_REDIRECT error to terminate the rendering of the route segment in which it was thrown. This is why you're having trouble using it in your function.

One way to handle this in TypeScript is to throw an error after calling redirect, and then catch this error in your client-side code to perform the redirect. Here's an example:

'use server'

// ...

export default async function signUpAction(
  prevState: SignUpFormStateT,
  formData: FormData
): Promise<SignUpFormStateT | undefined> {
  // ...

  try {
    // ...

    if (response.status === 200) {
      const authToken = response.data.token;
      cookies().set('auth', authToken, config);
      
      redirect('/confirmation/message');
      throw new Error('Redirecting...');
    }
    
  } catch (error) {
    if (error.message === 'Redirecting...') {
      // This error is expected, so we just re-throw it
      throw error;
    } else {
      console.error('Axios Error:', error);
      return handleAxiosError(error as AxiosError);
    }
  }

  return { error: true, message: 'An unknown error occurred.'};
};

Then, in your client-side code, you can catch this error and perform the redirect:

try {
  await signUpAction(prevState, formData);
} catch (error) {
  if (error.message === 'Redirecting...') {
    router.push('/confirmation/message');
  } else {
    // Handle other errors
  }
}

This way, you're still able to perform the redirect, but you're doing it on the client side where it's supported.

Please note that this is a workaround and may not be the best solution for all use cases. It's generally recommended to handle redirects on the client side in Next.js.