Next.js 14+TRPC+MongoDB注册时出现Response body锁定错误
Next.js 14 + TRPC + PayloadCMS 注册时500错误排查求助
在Windows 10环境下搭建了基于Next.js 14、TRPC、PayloadCMS和本地MongoDB的应用,用户注册时浏览器返回500错误,控制台提示TypeError: Response body object should not be disturbed or locked。排查发现请求未进入TRPC路由的mutation处理逻辑,已经尝试移除URL中的环境变量、改用httpLink替代httpBatchLink,但问题仍未解决。相关代码如下:
TRPC auth router
import { TRPCError } from "@trpc/server" import { getPayloadClient } from "../get-payload" import { AuthCredentialsValidator } from "../lib/validators/account-credentials-validator" import { publicProcedure, router } from "./trpc" import { NextResponse } from "next/server" export const authRouter = router({ createPayloadUser: publicProcedure .input(AuthCredentialsValidator) .mutation(async ({ input }) => { const { email, password } = input const payload = await getPayloadClient() // 检查用户是否已存在 const { docs: users } = await payload.find({ collection: "users", where: { email: { equals: email, }, }, }) if (users.length !== 0) throw new TRPCError({ code: "CONFLICT" }) await payload.create({ collection: "users", data: { email, password, role: "user", }, }) return NextResponse.json({ success: true, sentToEmail: email }) }), })
Providers.tsx
"use client" import { trpc } from "@/trpc/client" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { httpBatchLink } from "@trpc/client" import { PropsWithChildren, useState } from "react" const Providers = ({ children }: PropsWithChildren) => { const [queryClient] = useState(() => new QueryClient()) const [trpcClient] = useState(() => trpc.createClient({ links: [ httpBatchLink({ url: `${process.env.NEXT_PUBLIC_SERVER_URL}/api/trpc`, fetch(url, options) { return fetch(url, { // 浏览器控制台提示此处报错 ...options, credentials: "include", }) }, }), ], }) ) return ( <trpc.Provider client={trpcClient} queryClient={queryClient}> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> </trpc.Provider> ) } export default Providers
注册页面(Sign-up page)
"use client" import { Icons } from "@/components/Icons" import { Button, buttonVariants } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { cn } from "@/lib/utils" import { AuthCredentialsValidator, TAuthCredentialsValidator, } from "@/lib/validators/account-credentials-validator" import { trpc } from "@/trpc/client" import { zodResolver } from "@hookform/resolvers/zod" import { ArrowRight } from "lucide-react" import Link from "next/link" import { useForm } from "react-hook-form" const Page = () => { const { register, handleSubmit, formState: { errors }, } = useForm<TAuthCredentialsValidator>({ resolver: zodResolver(AuthCredentialsValidator), }) const { mutate, isLoading } = trpc.auth.createPayloadUser.useMutation({ onError: (err) => {}, onSuccess: ({}) => {}, }) const onSubmit = ({ email, password }: TAuthCredentialsValidator) => { mutate({ email, password }) } return (...此处省略JSX代码) } export default Page
package.json
{ "name": "digitalhippo", "version": "0.1.0", "private": true, "scripts": { "full": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon", "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types", "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@hookform/resolvers": "^3.3.4", "@payloadcms/bundler-webpack": "^1.0.6", "@payloadcms/db-mongodb": "^1.4.0", "@payloadcms/richtext-slate": "^1.4.0", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@tanstack/react-query": "4.36.1", "@trpc/client": "^10.45.0", "@trpc/next": "^10.45.0", "@trpc/react-query": "^10.45.0", "@trpc/server": "^10.45.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "cross-env": "^7.0.3", "dotenv": "^16.4.1", "lucide-react": "^0.314.0", "next": "14.1.0", "payload": "^2.9.0", "react": "^18", "react-dom": "^18", "react-hook-form": "^7.49.3", "sonner": "^1.3.1", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "ts-node": "^10.9.2", "zod": "^3.22.4" }, "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.1.0", "nodemon": "^3.0.3", "postcss": "^8", "tailwindcss": "^3.3.0", "typescript": "^5" } }
服务器配置
import * as trpcExpress from "@trpc/server/adapters/express" import express from "express" import { getPayloadClient } from "./get-payload" import { nextApp, nextHandler } from "./next-utils" import { appRouter } from "./trpc" const app = express() const PORT = Number(process.env.PORT) || 3000 const createContext = ({ req, res, }: trpcExpress.CreateExpressContextOptions) => ({ req, res, }) const start = async () => { const payload = await getPayloadClient({ initOptions: { express: app, onInit: async (cms) => { cms.logger.info(`Admin URL ${cms.getAdminURL()}`) }, }, }) app.use((req, res) => nextHandler(req, res)) app.use( "/api/trpc", trpcExpress.createExpressMiddleware({ router: appRouter, createContext, }) ) nextApp.prepare().then(() => { payload.logger.info("Next.js started") app.listen(PORT, async () => { payload.logger.info( `Next.js App URL: ${process.env.NEXT_PUBLIC_SERVER_URL}` ) }) }) } start()
内容的提问来源于stack exchange,提问作者Ioann Kurchin
相关产品推荐
相关产品推荐

