如何在Next.js中间件中调用tRPC过程实现用户重定向?
解决方案
方案一:使用tRPC Server Caller(推荐)
直接在中间件中创建tRPC服务端调用器,绕过客户端hooks直接执行tRPC过程。所有逻辑都在服务端运行,不会暴露Prisma或敏感数据到前端,安全且高效。
修改后的代码
import { getAuth, withClerkMiddleware } from "@clerk/nextjs/server"; import { type NextRequest, NextResponse } from "next/server"; import { appRouter } from "./server/api/root"; import { createTRPCContext } from "./server/api/trpc"; // 导入你项目中已有的tRPC上下文创建函数 export default withClerkMiddleware(async (req: NextRequest, opts) => { const { userId } = getAuth(req); if (userId) { // 构建与API路由一致的tRPC上下文 const ctx = await createTRPCContext({ req, resHeaders: new Headers() }); // 创建tRPC服务端调用器 const caller = appRouter.createCaller(ctx); try { // 调用tRPC过程查询用户数据 const user = await caller.ptusers.getOne({ id: userId }); // 用户不存在则重定向至注册页 if (!user) { return NextResponse.redirect(new URL("/sign-up", req.url)); } } catch (error) { // 处理查询错误(比如你的tRPC过程在用户不存在时抛出错误) return NextResponse.redirect(new URL("/sign-up", req.url)); } } return NextResponse.next(); }); export const config = { matcher: "/((?!_next/image|_next/static|favicon.ico|bg-signup.jpg|sign-up).*)", // 排除注册页避免循环重定向 };
关键说明
- 复用项目中已有的
tRPC上下文创建函数,确保权限校验、数据访问逻辑和API路由完全一致。 - 通过
appRouter.createCaller(ctx)创建服务端调用器,直接执行tRPC过程,无需依赖客户端hooks。
方案二:通过Edge API Route间接查询
如果不想在中间件中直接依赖tRPC路由,可以创建一个Edge API Route封装数据库查询逻辑,中间件通过HTTP请求调用该API。
步骤1:创建Edge API Route(app/api/check-user/route.ts)
import { getAuth } from "@clerk/nextjs/server"; import { NextResponse } from "next/server"; import { prisma } from "@/server/db"; export async function GET(req: Request) { const { userId } = getAuth(req); if (!userId) return NextResponse.json({ exists: false }, { status: 401 }); const user = await prisma.ptusers.findUnique({ where: { id: userId } }); return NextResponse.json({ exists: !!user }); }
步骤2:在中间件中调用该API
import { getAuth, withClerkMiddleware } from "@clerk/nextjs/server"; import { type NextRequest, NextResponse } from "next/server"; export default withClerkMiddleware(async (req: NextRequest, opts) => { const { userId } = getAuth(req); if (userId) { try { // 调用Edge API查询用户是否存在 const res = await fetch(new URL("/api/check-user", req.url), { headers: { ...req.headers, // 传递Clerk认证头,确保API能获取用户ID "Content-Type": "application/json", }, }); const { exists } = await res.json(); if (!exists) { return NextResponse.redirect(new URL("/sign-up", req.url)); } } catch (error) { return NextResponse.redirect(new URL("/sign-up", req.url)); } } return NextResponse.next(); }); export const config = { matcher: "/((?!_next/image|_next/static|favicon.ico|bg-signup.jpg|sign-up).*)", };
关键说明
- 通过API Route隔离数据库操作,中间件仅做HTTP请求,降低耦合度。
- 需传递认证头,确保API Route能正确识别当前用户。
- 性能略低于方案一,因为多了一次内部HTTP请求。
额外注意事项
- Next.js中间件运行在Edge Runtime,确保你的依赖(如Prisma)支持Edge环境(Prisma 4.16+提供Edge Client)。
- 务必在中间件匹配规则中排除注册页,避免出现循环重定向问题。
内容的提问来源于stack exchange,提问作者zak
相关产品推荐
相关产品推荐

