15

In my Next.js app, I need to get some parameters from the URL and use them to set a server-side cookie.

But I'm not even sure if I can set a server-side cookie this way. Is this possible at all? If yes, any hint or suggestion would be really appreciate.

4 Answers 4

20

Use cookies-next by the command npm i cookies-next

Server Side Example

import React from 'react'
import { getCookies, getCookie, setCookies, removeCookies } from 'cookies-next';

const Home = () => {
  return (
    <div>page content</div>
  )
}

export const getServerSideProps = ({ req, res }) => {
    setCookies('test', 'value', { req, res, maxAge: 60 * 6 * 24 });
    getCookie('test', { req, res});
    getCookies({ req, res});
    removeCookies('test', { req, res});
  return { props: {}};
}

export default Home

Client Side Example

import { getCookies, setCookies, removeCookies } from 'cookies-next';
// we can use it anywhere
getCookies();
getCookie('key');
setCookies('key', 'value');
removeCookies('key');

for more information see https://www.npmjs.com/package/cookies-next

9

Next applications are not working like usual server applications. Although, you can make a custom flow like using a DBaaS. Or, if you only want to restrict client access to that cookie, I think it's okay to keep them with httponly flag (you should be the one who decides that). In this way, you can only read that cookie at the server.

Here is an example usage with a NextJs serverless API endpoint:

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  const { yourParameter } = req.query;
  res.setHeader("set-cookie", `yourParameter=${yourParameter}; path=/; samesite=lax; httponly;`)
  res.redirect('/');
}

You can apply the same logic to the getServerSideProps callback or other places that runs at the server.

1
  • +1 thanks. Saved my backside on this one. To this day (Next 13.1.1), this is the best way to set cookies within the API route, and not middleware (which the docs only show).
    – tudor14
    Commented Jan 8, 2023 at 3:09
0

I ran into a very similar problem. My use-case was this: I am building an app where users can have multiple "memberships" at different orgs. The current org_id is stored in a separate cookie from the session token. When the user logs in, the following logic should apply:

  • If they have no memberships, clear any session_org_id and redirect to Create Org
  • If they have one membership, set the session_org_id and redirect to Home
  • If they have multiple memberships, check the session org_id and if it matches one, redirect to Home, otherwise clear it and redirect to Choose Profile

Warning: NextJS App Router jargon ahead

This is very difficult to accomplish from within a server-side component, since server actions cannot be called from components... which is why my many many efforts to implement this logic IN choose-profile/page.tsx component failed. What works, however, is to define a separate "post-auth" route handler that does this logic, since server actions CAN be called from route handers

// post-auth/route.ts

/// These server actions modify the session_org_id using import { cookies } from "next/headers";
import { clearSessionOrg, setSessionOrg } from "actions/setSessionOrg";
import { auth } from "auth";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
/// gets the user's memberships from the remote API server
import { getMemberships } from "ssr/get_memberships";

/// This function is responsible for checking a user's memberships;
/// setting or clearing the session org if necessary,
/// and then redirecting the user to the appropriate page
export async function GET() {
  const cookieStore = cookies();
  const session = await auth();

  if (!session?.user) {
    redirect("/sign-in");
  }
  const memberships = await getMemberships(cookieStore);

  if (!memberships?.length) {
    redirect("/create-org");
  }
  if (memberships?.length == 1) {
    await setSessionOrg(memberships[0].org_id);
    redirect("/client");
  }
  let sessionOrgId = cookieStore.get("versa.session_org_id")?.value;
  if (sessionOrgId && memberships.find((m) => m.org_id === sessionOrgId)) {
    redirect("/client");
  } else {
    await clearSessionOrg();
    redirect("/choose-profile");
  }
}


// actions/setSessionOrg.ts

"use server";

import { cookies } from "next/headers";

export const setSessionOrg = async (orgId: string) => {
  "use server";
  cookies().set("my-app.session_org_id", orgId, {
    httpOnly: true,
    sameSite: "none",
    secure: true,
  });
};

export const clearSessionOrg = async () => {
  "use server";
  cookies().delete("my-app.session_org_id");
};


Boom. Post login and anywhere I need to confirm a user's membership status (such as in my (authorized) parent layout, I can redirect to "post-auth" instead of "choose-profile"

-2

you can use 'next/headers' cookies

NextJS Docs

but it needs to be mentioned:

HTTP does not allow setting cookies after streaming starts, so you must use .set() in a Server Action or Route Handler.

Server Side Example

import { cookies } from 'next/headers'

async function RootLayout({ children }: RootLayoutProps) {
  const cookieStore = cookies()
  const testCookie = cookieStore.get('test')

  return { props: { testCookie }};
}

middleware.ts

export function middleware(request: NextRequest) {
  const url = new URL(request.url) // here you can destruct needed params from URL

  const cookieStore = cookies()
  cookieStore.set('key', 'value')  
}
1
  • 4
    you cannot set your cookies with cookieStore.set('key', 'value') in middleware. It will cause "Cookies can only be modified in a Server Action or Route Handler."
    – xtru1990
    Commented Mar 13 at 6:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.