Skip to main content

Cookie Session Management

The Reflow Auth library can be used as a generic session library for your Next.js app. The data is persisted in an encrypted http-only cookie and works in both the Node.js and Edge runtimes. This saves you from having to include a separate library like iron-session.

As an example, let's build a button that counts how many times it has been clicked. You can find the full example here.

Note that you can read session data in any server component, but since it is stored in a cookie, you can update the session only in places where cookies can be set, like in server actions and route handlers.

For this reason, our counter update routes will be defined in two route handlers.

app/api/counter/increment/route.ts
import getAuth from "@/auth";

export async function POST() {
const auth = getAuth();
await auth.set("counter", (await auth.get("counter", 0)) + 1);
return Response.json({ success: true });
}

The first handler reads and increments the session using auth.get and auth.set.

app/api/counter/clear/route.ts
import getAuth from "@/auth";

export async function POST() {
const auth = getAuth();
await auth.forget("counter");
return Response.json({ success: true });
}

The second handler clears the data with auth.forget.

To issue fetch requests to the above endpoints, we will define two server components.

app/components/IncrementButton.ts
"use client";

export default function IncrementButton() {
async function increment() {
await fetch("/api/counter/increment", {
credentials: "include",
method: "POST",
});
location.reload();
}
return <button onClick={increment}>Increment counter</button>;
}
app/components/ClearButton.ts
"use client";

export default function ClearButton() {
async function increment() {
await fetch("/api/counter/clear", {
credentials: "include",
method: "POST",
});
location.reload();
}
return <button onClick={increment}>Clear counter</button>;
}

And lastly, to bring everything together, we will build a page which displays the current value of the counter and both actions.

app/page.tsx
import IncrementButton from "@/app/components/IncrementButton";
import ClearButton from "@/app/components/ClearButton";
import getAuth from "@/auth";

export default async function Page() {
const auth = getAuth();

return (
<>
<p>Counter value is: {await auth.get("counter", 0)}</p>
<IncrementButton />
<br />
<ClearButton />
</>
);
}