如何在TRPC独立服务器中为注册用户存储会话Cookie?
问题
我是一名前端开发者,正在学习后端与TRPC,已编写如下用户注册流程代码:
注册流程代码
export const registration = publicProcedure .input(RegistrationFormSchema) .mutation(async ({ input: { email, password }, ctx }) => { const existingUser = await prisma.user.findUnique({ where: { email } }); if (existingUser) { Failures.throwCustomError( { code: 'CONFLICT', message: `Email ${email} already exists` }, { procedure: PROCEDURE_NAME }, ); } try { const hashedPassword = await hash(password, SALT_ROUNDS); const user = await prisma.user.create({ data: { email, password: hashedPassword } }); console.log({ ctx }); return { success: true, data: user }; } catch (error) { if (error instanceof Error) { Failures.handleServerError(error, { procedure: PROCEDURE_NAME, metadata: { problem: 'Error while registering user.' }, }); } } });
疑问
查阅TRPC授权章节后,发现文档仅介绍了如何传递授权用户信息,但Context对象并未暴露setHeader之类的方法。请问如何在TRPC独立适配器中将会话存储到Cookie中?或者我的思路存在问题?
解决方案
你的思路没问题,TRPC本身不直接提供设置Cookie的方法,但可以通过在Context中注入响应对象来实现操作Cookie的能力,具体步骤如下:
1. 定义包含响应对象的Context
根据你使用的独立适配器(如express、node-http、fastify),在创建Context时把请求/响应对象注入进去,以express适配器为例:
import type { inferAsyncReturnType } from '@trpc/server'; import type { CreateExpressContextOptions } from '@trpc/server/adapters/express'; // 假设你已经初始化了Prisma实例 import { prisma } from './prisma'; export const createContext = ({ req, res }: CreateExpressContextOptions) => { return { req, res, // 把响应对象注入Context prisma, }; }; export type Context = inferAsyncReturnType<typeof createContext>;
2. 在注册Mutation中设置Cookie
生成会话凭证(比如JWT)后,通过Context中的响应对象设置Cookie:
import jwt from 'jsonwebtoken'; // 用JWT生成会话token,也可以用其他会话方案 export const registration = publicProcedure .input(RegistrationFormSchema) .mutation(async ({ input: { email, password }, ctx }) => { const existingUser = await ctx.prisma.user.findUnique({ where: { email } }); if (existingUser) { Failures.throwCustomError( { code: 'CONFLICT', message: `Email ${email} already exists` }, { procedure: PROCEDURE_NAME }, ); } try { const hashedPassword = await hash(password, SALT_ROUNDS); const user = await ctx.prisma.user.create({ data: { email, password: hashedPassword } }); // 生成会话token const sessionToken = jwt.sign({ userId: user.id }, 'your-secure-secret-key', { expiresIn: '7d' }); // 通过ctx.res设置Cookie(express适配器的res.cookie方法) ctx.res.cookie('session', sessionToken, { httpOnly: true, // 禁止前端JS访问,防范XSS secure: process.env.NODE_ENV === 'production', // 生产环境强制HTTPS maxAge: 7 * 24 * 60 * 60 * 1000, // 7天有效期 sameSite: 'lax', // 防范CSRF }); return { success: true, data: user }; } catch (error) { if (error instanceof Error) { Failures.handleServerError(error, { procedure: PROCEDURE_NAME, metadata: { problem: 'Error while registering user.' }, }); } } });
3. 不同适配器的Cookie设置差异
- node-http原生适配器:需要手动拼接
Set-Cookie响应头:
ctx.res.setHeader('Set-Cookie', `session=${sessionToken}; HttpOnly; Secure=${process.env.NODE_ENV === 'production'}; Max-Age=${7*24*60*60}; SameSite=Lax`);
- Fastify适配器:使用
ctx.res.setCookie方法(Fastify内置的Cookie工具):
ctx.res.setCookie('session', sessionToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 7 * 24 * 60 * 60, sameSite: 'lax', });
内容的提问来源于stack exchange,提问作者seven
相关产品推荐
相关产品推荐

