How to set cookie in ct3 app router?

I tried to use cookies form 'next/headers' and it's not working.
No description
Solution:
I found the solution.
Jump to solution
6 Replies
Chico
Chico8mo ago
Cookies are read only in route handlers you need to use the response object to set them https://nextjs.org/docs/app/building-your-application/routing/route-handlers
Routing: Route Handlers
Create custom request handlers for a given route using the Web's Request and Response APIs.
NekoChan
NekoChan8mo ago
Thx you for your answer, I just read it, but how do I return new Response with trpc?
No description
NekoChan
NekoChan8mo ago
No description
NekoChan
NekoChan8mo ago
No description
Solution
NekoChan
NekoChan8mo ago
I found the solution.
NekoChan
NekoChan8mo ago
1. change unstable_httpBatchStreamLink to httpBatchLink in trpc/react.tsx and trpc/server.ts ( https://discord.com/channels/966627436387266600/966627439390380064/1165106604128092190 ) 2. pass resHeader to createTRPCContext function
// src\app\api\trpc\[trpc]\route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
import type { NextRequest } from 'next/server'

import { env } from '~/env.mjs'
import { appRouter } from '~/server/api/root'
import { createTRPCContext } from '~/server/api/trpc'

const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: ({ resHeaders }) => createTRPCContext({ req, resHeaders }), ⬅️⬅️⬅️⬅️⬅️
onError:
env.NODE_ENV === 'development'
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`,
)
}
: undefined,
})

export { handler as GET, handler as POST }
// src\app\api\trpc\[trpc]\route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
import type { NextRequest } from 'next/server'

import { env } from '~/env.mjs'
import { appRouter } from '~/server/api/root'
import { createTRPCContext } from '~/server/api/trpc'

const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: ({ resHeaders }) => createTRPCContext({ req, resHeaders }), ⬅️⬅️⬅️⬅️⬅️
onError:
env.NODE_ENV === 'development'
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`,
)
}
: undefined,
})

export { handler as GET, handler as POST }
3. update createTRPCContext and createInnerTRPCContext functions ( I'm using cookie: https://www.npmjs.com/package/cookie )
// src\server\api\trpc.ts
export const createTRPCContext = async (opts: {
req: NextRequest
resHeaders: Headers ⬅️⬅️⬅️⬅️⬅️
}) => {
return await createInnerTRPCContext({
headers: opts.req.headers,
resHeaders: opts.resHeaders, ⬅️⬅️⬅️⬅️⬅️
})
}

export const createInnerTRPCContext = async (opts: CreateContextOptions) => {
const serverAuthSession = await getServerAuthSession()
⬇️⬇️⬇️⬇️⬇️
const cookies = {
get: (name?: string) => {
const cookiesHeader = opts.headers.get('Cookie')
if (!cookiesHeader) return null
const cookies = parse(cookiesHeader)
return name ? cookies[name] ?? null : cookies
},
has: (name: string) => {
const cookiesHeader = opts.headers.get('Cookie')
if (!cookiesHeader) return false
const cookies = parse(cookiesHeader)
return name in cookies
},
set: (name: string, value: string, options?: CookieSerializeOptions) => {
opts.resHeaders.append('Set-Cookie', serialize(name, value, options))
},
clear: (name: string) => {
opts.resHeaders.append('Set-Cookie', serialize(name, '', { maxAge: -1 }))
},
}

return {
session: serverAuthSession,
headers: opts.headers,
cookies: cookies, ⬅️⬅️⬅️⬅️⬅️
db,
}
}
// src\server\api\trpc.ts
export const createTRPCContext = async (opts: {
req: NextRequest
resHeaders: Headers ⬅️⬅️⬅️⬅️⬅️
}) => {
return await createInnerTRPCContext({
headers: opts.req.headers,
resHeaders: opts.resHeaders, ⬅️⬅️⬅️⬅️⬅️
})
}

export const createInnerTRPCContext = async (opts: CreateContextOptions) => {
const serverAuthSession = await getServerAuthSession()
⬇️⬇️⬇️⬇️⬇️
const cookies = {
get: (name?: string) => {
const cookiesHeader = opts.headers.get('Cookie')
if (!cookiesHeader) return null
const cookies = parse(cookiesHeader)
return name ? cookies[name] ?? null : cookies
},
has: (name: string) => {
const cookiesHeader = opts.headers.get('Cookie')
if (!cookiesHeader) return false
const cookies = parse(cookiesHeader)
return name in cookies
},
set: (name: string, value: string, options?: CookieSerializeOptions) => {
opts.resHeaders.append('Set-Cookie', serialize(name, value, options))
},
clear: (name: string) => {
opts.resHeaders.append('Set-Cookie', serialize(name, '', { maxAge: -1 }))
},
}

return {
session: serverAuthSession,
headers: opts.headers,
cookies: cookies, ⬅️⬅️⬅️⬅️⬅️
db,
}
}
and you can use cookies in tRPC context, for example:
signIn: publicProcedure
.input(credentialSchema)
.mutation(async ({ ctx, input }) => {
...

ctx.cookies.set(SESSION_TOKEN_COOKIE, sessionToken, {
expires: sessionTokenExpires,
path: '/',
})

...
}),
signIn: publicProcedure
.input(credentialSchema)
.mutation(async ({ ctx, input }) => {
...

ctx.cookies.set(SESSION_TOKEN_COOKIE, sessionToken, {
expires: sessionTokenExpires,
path: '/',
})

...
}),