Share session and cookies between Next and Remix
I have been migrating a Next.js app to Remix for a few months. I already wrote how to run them together in the same Express server.
The next thing I had to do was see how to share the authenticated user data between Next and Remix.
Share Cookies
The first thing is to share cookies. Luckily, they are running on the same server, so they share the domain, which means any request to both of them will come with the cookies. So we could read them and set them in both apps.
To simplify working with cookies what I did was to create a cookie object using Remix createCookie
function, something like this:
export let sessionCookie = createCookie("session", {
secrets: [requireEnv("COOKIE_SECRET", "s3cr3t")],
httpOnly: true,
sameSite: "lax",
path: "/",
secure: isProduction(),
domain: requireEnv("COOKIE_DOMAIN", "localhost"),
maxAge: 60 * 60 * 24, // 1 day
});
Now, I could import sessionCookie
in Remix and use it as documented or in Next, and use it like this in API routes:
// src/pages/api/endpoint.ts
import { sessionCookie } from "~/cookies";
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// more code
// first I parse the cookie from the Cookie header
let session = await sessionCookie.parse(req.headers.cookie)
// more code
// and to send it in the response I serialize it and set header
res.setHeader("Set-Cookie", await sessionCookie.serialize(session));
// more code
}
And I could even use it getServerSideProps
.
// src/pages/something.tsx
import { sessionCookie } from "~/cookies";
export let getServerSideProps: GetServerSideProps = async ({ req }) => {
// more code
// first I parse the cookie from the Cookie header
let session = await sessionCookie.parse(req.headers.cookie)
// more code
// and to send it in the response I serialize it and set header
res.setHeader("Set-Cookie", await sessionCookie.serialize(session));
// more code
}
As you can see, it's basically the same. We parse the cookie header, serialize the session, and set the Set-Cookie header.
Sessions
Sessions use cookies internally. Even if you don't store the session data in the cookie, you need a cookie to keep the session ID.
This means to create a session, we will use the cookie we made before
import { sessionCookie as cookie } from "~/cookies";
export let sessionStorage = createCookieSessionStorage({ cookie });
I also like to re-export parts of sessionStorage
and create a wrapper of sessionStorage.getSession
to receive a Request object.
export async function getSession(request: Request) {
return await sessionStorage.getSession(request.headers.get("Cookie"));
}
export let { commitSession, destroySession } = sessionStorage;
But that getSession
will work on Remix, not in Next, so I created a new wrapper called getToken
since that's the only thing I store in the session anyway.
export async function getToken(request: NextPageContext["req"]) {
let session = await sessionStorage.getSession(request.headers.cookie ?? "");
return session.get("token") as string | null;
}
As you can see, getToken
receives a Next.js request (it's using the NextPageContext
type but also works with API routes requests).
Now, we can get the API token from the session in Next in a simpler way:
// src/pages/api/endpoint.ts
import { getToken } from "~/session";
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
let token = await getToken(req)
// more code
}
// src/pages/something.tsx
import { sessionCookie } from "~/cookies";
export let getServerSideProps: GetServerSideProps = async ({ req }) => {
let token = await getToken(req)
// more code
}
I can use Remix cookies and sessions, which are really easy to work with within both Next and Remix.
Bonus: Authentication
Additionally, the first part we migrated was the authentication, using Remix Auth. It was straightforward to set up. It stored the API token automatically in the session. Then, I could use the getToken
method in Next or auth.isAuthenicated
from Remix Auth in the Remix app.