skip to Main Content

Hi I’m trying to implement the logout logic, my session token is stored in the cookies, which I want to clear if the token is no longer valid. But next.js is not allowing me to do so, even so their documentation says that I can update cookies from a server action. I feel most likely the way I’m invoking server actions is the issue, if so, can u help with the better way.

This is a little confusing to me, all comments are welcomed.

import React from 'react';
import { gql } from '@urql/core';
import { cookies } from 'next/headers';
import { getClient } from '../actions';
import { redirect } from 'next/navigation';


const PokemonsQuery = gql`
    query Me {
        me {
            id
            username
            roles
            createdAt
            updatedAt
        }
    }
`;

export default async function Home() {
    const result = await getClient().query(PokemonsQuery, {}).toPromise();
    
    async function logout() {
        'use server'
        // remove cookie
        cookies().delete('access_token');
        // redirect to login page
        redirect('/login');
    }

    // if any issue happens then logout
    if(result.error && result.error.networkError){
        await logout();
    }
    
    return (
        <main>
            <h1>This is rendered as part of an RSC</h1>
            <ul>
                <div>
                    <p>id: {result.data.me.id}</p>
                    <p>username: {result.data.me.username}</p>
                    <p>roles: {result.data.me.roles.join(', ')}</p>
                    <p>createdAt: {result.data.me.createdAt}</p>
                    <p>updatedAt: {result.data.me.updatedAt}</p>
                </div>
            </ul>
        </main>
    );
}

error:

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 $$ACTION_0 (./src/app/server/page.tsx:125:67)
    at Home (./src/app/server/page.tsx:41:15)

2

Answers


  1. It seems like you are trying to do a cookie write operation inside a Server Component and not a Server Action. Though, you can read the cookies inside a Server Compononent, you can’t write it. So, you can update your code to be:-

    page.tsx

    import React from 'react';
    import { gql } from '@urql/core';
    import { cookies } from 'next/headers';
    import { getClient } from '../actions';
    import { redirect } from 'next/navigation';
    
    
    const PokemonsQuery = gql`
        query Me {
            me {
                id
                username
                roles
                createdAt
                updatedAt
            }
        }
    `;
    
    export default async function Home() {
        const result = await getClient().query(PokemonsQuery, {}).toPromise();
        
        async function logout() {
            deleteCookie('cookieName); // Add import of this from actions.ts file 
            // redirect to login page
            redirect('/login');
        }
    
        // if any issue happens then logout
        if(result.error && result.error.networkError){
            await logout();
        }
        
        return (
            <main>
                <h1>This is rendered as part of an RSC</h1>
                <ul>
                    <div>
                        <p>id: {result.data.me.id}</p>
                        <p>username: {result.data.me.username}</p>
                        <p>roles: {result.data.me.roles.join(', ')}</p>
                        <p>createdAt: {result.data.me.createdAt}</p>
                        <p>updatedAt: {result.data.me.updatedAt}</p>
                    </div>
                </ul>
            </main>
        );
    }
    

    actions.ts

    'use server'
     
    import { cookies } from 'next/headers'
     
    async function deleteCookie(name) {
      cookies().delete(name)
    }
    
    Login or Signup to reply.
  2. There are scenarios where you can use server actions, but not anywhere in your code, Read more.

    Just create a route handler /api/access_token/route.ts:

    import { cookies } from "next/headers"
    
    export async function DELETE(req: Request): Promise<Response>() {
       cookies().delete("access_token");
       return Response.json({ message: "success" }, {status: 200}); 
    }
    

    And then call it inside your logout function:

    ...
    async function logout() {
       // if it requires a baseUrl before the route handler path, then the baseUrl is either localhost in development or your domain in production
       await fetch(`/api/access_token`, {
          method: "DELETE"
       })
       // redirect to login page
       redirect('/login');
    }
    ...
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search