Hey! Trying to deploy my next js project onto Vercel. Everything runs fine locally, but I get this error with an unhelpful digest property.
[Error]: Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers
at Proxy.callable (/var/task/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-page-experimental.runtime.prod.js:75:33099)
My app/page.tsx is a home page that calls a route handler to check if there is a current valid cookie if not redirect to sign-in. My app/sign-in/page.tsx does something similar, where it checks for a valid cookie then redirects to home if there is.
import { request } from '@/lib/utils'
import { Chat } from '@/components/chat'
import { headers } from 'next/headers'
import { redirect } from 'next/navigation'
async function getSession() {
"use client"
const options = {
method: "GET",
headers: headers(),
};
console.log("check 1");
const session = await fetch(`${process.env.FRONTEND_BASE_URL}/api/user`, options)
console.log("check 2);
if (session.status === 401) {
console.log("redirecting");
redirect(`/sign-in`)
} else if (session.status === 402) { // invalid cookie code
const options = {
method: "GET",
};
await fetch(`${process.env.FRONTEND_BASE_URL}/api/session`, options)
redirect(`/sign-in`)
}
return await session.json()
}
export default async function IndexPage() {
const session = await getSession()
const user = session.user
return <Chat />
}
I get the headers cannot be modified error between check 1 and check 2. Problem is I need to pass headers() to the route handler or it won't be able to pickup the cookies.
api/user/route.tsx:
import { sealData, unsealData } from 'iron-session/edge';
import { NextRequest, NextResponse } from 'next/server'
import { request as serverRequest } from '@/lib/utils'
import { cookies } from 'next/headers'
export async function GET(request: NextRequest) {
"use server";
const badResponse = new Response(null, {
status: 401,
})
let goodHeaders = {}
const cookie = request.cookies.get('voyager-session')?.value
const session = cookie
? await unsealData(cookie, {
password: process.env.SESSION_PASSWORD as string,
})
: { accessToken: null, refreshToken: null };
if (!cookie || Object.keys(session).length === 0) {
return badResponse
}
const { accessToken, refreshToken } = JSON.parse(String(session))
if (!accessToken || !refreshToken) {
return new Response(JSON.stringify({ msg: 'Invalid cookie, need to delete' }), {
status: 402,
})
}
const options = {
method: 'GET',
url: `/me?token=${accessToken}&refresh=${refreshToken}`,
}
const newSession = await serverRequest(options)
if (!newSession) {
return new Response(JSON.stringify({ msg: 'Expired cookie, need to delete' }), {
status: 402,
})
}
let result = {
...newSession,
token: accessToken
}
return new Response(JSON.stringify(result), {
status: 200,
headers: {
...goodHeaders,
'Content-Type': 'application/json'
}
});
};
async function process_auth(session: any) {
const sessionString = JSON.stringify(session);
const encryptedSession = await sealData(sessionString, {
password: process.env.SESSION_PASSWORD as string,
});
return encryptedSession;
}
If I don't pass headers() in my fetch to api/user in app/page, then const cookie = request.cookies.get('voyager-session')?.value comes as null and the error routes my page back to sign-in even though there is a cookie I can manually see. So how do I get the headers with cookies in my route handler?
Any help would be greatly appreciated ❤️