You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js T3栈应用中tRPC Mutation的500内部服务器错误排查

T3栈应用调用auth.UserChecker mutation时返回500内部服务器错误

技术栈

  • Next.js
  • NextAuth.js
  • Prisma
  • Tailwind CSS
  • tRPC

错误场景

调用auth.UserChecker mutation进行登录凭证验证时,服务器返回500内部服务器错误。注册页与登录页结构一致,仅输入项不同。错误触发位置为src/server/api/routers/auth.ts中的UserChecker路由,被src/components/auth/login.tsx和register.tsx调用,应用其余配置为默认。

相关代码

login.tsx

"use client";
import { api } from "@/trpc/react";
function Login() {
  const { toast } = useToast();
  const UserChecker =  api.auth.UserChecker.useMutation({
    onSuccess: (response) => {
      console.log("Mutation successful", response);
      // Handle successful mutation, e.g., redirect or update UI
    },
    onError: (error) => {
      console.log("Mutation error", error);
      toast({
        variant: "destructive",
        title: "Error", 
        
        description: "An error occurred during login."
      });
    }
  })
  const [user, setNewUser] = useState({
    email: "",
    password: "",
  });
  const loginForm = z.object({
    email: z
      .string()
      .min(1, { message: "This field has to be filled." })
      .email("This is not a valid email."),
    password: z.string().min(1, { message: "This field has to be filled" }),
  });

   async function LoginFormSubmet() {
    const validUser = loginForm.safeParse(user);
    
      if (validUser.data) {
        console.log("4");

        const Data = validUser.data;
        UserChecker.mutate(Data)
          
      }
    
  }
  return (
    <>
      
       Input will be here.

      <Button
        
        className=" relative w-48"
        onClick={(e) => {
          e.preventDefault();
          LoginFormSubmet();
        }}
      >
        {" "}
        Register
      </Button>
     
    </>
  );
}

export default Login;

auth.ts(tRPC路由)

import { object, z } from "zod";
import bcrypt from 'bcryptjs';

import {
  createTRPCRouter,
  protectedProcedure,
  publicProcedure,
} from "@/server/api/trpc";

export const authRouter = createTRPCRouter({
    UserChecker:publicProcedure.input(z.object({email:z.string() ,password:z.string()}))
    .mutation(async({ctx,input})=>{
        console.log(input);
        return JSON.parse(JSON.stringify({data:"password"}));

    }),
    makeNewUser:publicProcedure.input(z.object({
        name: z.string().min(1 ,{message:"This field has to be filled."}),
        dateOfBirth:z.string().min(1 ,{message:"This field has to be filled."}),
        email:z.string().min(1, { message: "This field has to be filled." }).email("This is not a valid email."),
        password:z.string().min(5 ,{message:"password  must be at least 5 characters"}),
        confirmPassword:z.string()

    })).mutation( async({ctx ,input})=>{
        const FindUser = await ctx.db.user.findUnique({
            where:{
                email:input.email
            }
        })

        if(FindUser){return{error:"this user exists"}}
        if(!FindUser){
            const salt = await bcrypt.genSalt(10);
            const passwordHash =  await bcrypt.hash(input.password, salt);
        const newUser = await ctx.db.user.create({
            data:{
                name:input.name,
                email:input.email,
                dateOfBirth:input.dateOfBirth,
                password:passwordHash
            }
        })

        }


    }),
    findUser:publicProcedure.input(z.object({
        email:z.string().min(1, { message: "This field has to be filled." }).email("This is not a valid email.")
    })).query(async({ ctx ,input}) =>{
        return ctx.db.user.findUnique({where:{email:input.email}})
    }),

    
    findUserValidation:publicProcedure.input(z.object({
        email:z.string().min(1, { message: "This field has to be filled." }).email("This is not a valid email."),
        password:z.string(),

    })).mutation(async({ ctx ,input}) =>{
        const FoundUser = await ctx.db.user.findUnique({where:{email:input.email}})
        if(!FoundUser){return {error:"user Does not exist"}}
        const CheckPassword = await bcrypt.compare(input.password ,FoundUser.password)
        if(!CheckPassword){return{error:"user Does not exist"}}
        return{input}
    })
 
});

问题排查与解决方案

  1. 修复前端导入缺失
    login.tsx中使用了useState和useToast但未导入,会导致前端运行时错误,影响请求发送。添加缺失的导入:
import { useState } from "react";
import { useToast } from "@/components/ui/toast"; // 路径根据实际项目调整
  1. 简化UserChecker返回逻辑
    原代码中JSON.parse(JSON.stringify({data:"password"}))属于多余操作,可能引发序列化异常,直接返回对象即可:
UserChecker: publicProcedure.input(z.object({email: z.string(), password: z.string()}))
  .mutation(async({ctx, input}) => {
    console.log(input);
    return { data: "password" };
  }),
  1. 检查tRPC上下文配置
    确保src/server/api/trpc.ts中正确注入Prisma客户端到上下文,比如:
import { prisma } from "@/server/db";
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
  // 其他上下文逻辑
  return {
    db: prisma,
    // ...其他上下文内容
  };
};

如果上下文未正确注入db,调用ctx.db会直接抛出错误导致500。

  1. 查看服务器端日志
    启动应用后观察终端输出,确认UserChecker中的console.log(input)是否打印。若未打印,说明路由未正确匹配;若打印后仍报错,查看具体错误信息(如Prisma连接失败、数据库字段缺失等)。

  2. 验证Prisma配置
    检查prisma/schema.prisma中的User模型是否包含email、password等必要字段,且已执行prisma migrate dev同步数据库结构。

内容的提问来源于stack exchange,提问作者OGZY MMO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:25:57