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

使用Next.js+tRPC实现注册接口遇resolver is not a function错误

问题排查与T3栈注册功能最优实现

一、先解决「TypeError: resolver is not a function」500错误

这个报错的核心是你把tRPC路由和普通Next.js API路由的逻辑搞混了,常见坑点:

  1. 错误地把tRPC Router直接导出为Next.js API路由,没用到tRPC的createNextApiHandler包装
  2. signUpRouter里的procedure resolver定义格式不对(比如写成了对象而非函数)
  3. 把tRPC子路由合并到appRouter的方式出错

快速修复步骤

情况1:你想用普通Next.js API做注册(不通过tRPC)

直接把pages/api/auth/signup.ts改成标准API格式,别掺和tRPC:

// pages/api/auth/signup.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "../../../server/db/client";
import bcrypt from "bcrypt";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== "POST") return res.status(405).json({ msg: "只接受POST请求" });

  const { email, password, name } = req.body;
  if (!email || !password || !name) return res.status(400).json({ msg: "必填字段不能为空" });

  try {
    // 检查用户是否已存在
    const existingUser = await prisma.user.findUnique({ where: { email } });
    if (existingUser) return res.status(409).json({ msg: "该邮箱已注册" });

    // 加密密码
    const hashedPwd = await bcrypt.hash(password, 10);
    const user = await prisma.user.create({
      data: { email, name, hashedPassword: hashedPwd }
    });

    return res.status(201).json({ userId: user.id });
  } catch (err) {
    console.error(err);
    return res.status(500).json({ msg: "服务器内部错误" });
  }
}

情况2:你想用tRPC处理注册(推荐,符合T3栈规范)

T3栈的tRPC路由都要通过主入口pages/api/trpc/[trpc].ts处理,单独的signup.ts不该是tRPC路由文件:

  1. 定义tRPC注册路由:
// server/api/routers/auth.ts
import { z } from "zod";
import { router, publicProcedure } from "../trpc";
import { prisma } from "../../db/client";
import bcrypt from "bcrypt";

export const authRouter = router({
  signUp: publicProcedure
    .input(z.object({
      email: z.string().email(),
      password: z.string().min(6),
      name: z.string().min(1)
    }))
    .mutation(async ({ input }) => {
      const { email, password, name } = input;
      
      const existingUser = await prisma.user.findUnique({ where: { email } });
      if (existingUser) throw new Error("该邮箱已注册");

      const hashedPwd = await bcrypt.hash(password, 10);
      const user = await prisma.user.create({
        data: { email, name, hashedPassword: hashedPwd }
      });

      return { userId: user.id, email: user.email };
    })
});
  1. 合并到根路由:
// server/api/root.ts
import { router } from "./trpc";
import { authRouter } from "./routers/auth";

export const appRouter = router({
  auth: authRouter
});

export type AppRouter = typeof appRouter;
  1. 前端用tRPC客户端调用(别用fetch):
// 前端组件示例
import { api } from "../utils/api";

const SignUpForm = () => {
  const signUpMutate = api.auth.signUp.useMutation({
    onSuccess: () => window.location.href = "/",
    onError: (err) => alert(err.message)
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    signUpMutate.mutate({
      email: "test@example.com",
      password: "123456",
      name: "张三"
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 表单输入框省略 */}
      <button type="submit" disabled={signUpMutate.isLoading}>
        {signUpMutate.isLoading ? "注册中..." : "注册"}
      </button>
    </form>
  );
};

二、T3栈注册功能最优实践(结合NextAuth自动登录)

既然用了NextAuth,注册完成后直接自动登录才是流畅体验,完整实现如下:

1. 后端tRPC路由(集成NextAuth登录)

// server/api/routers/auth.ts
import { z } from "zod";
import { router, publicProcedure } from "../trpc";
import { prisma } from "../../db/client";
import bcrypt from "bcrypt";
import { signIn } from "next-auth/react";

export const authRouter = router({
  signUp: publicProcedure
    .input(z.object({
      email: z.string().email(),
      password: z.string().min(6),
      name: z.string().min(1)
    }))
    .mutation(async ({ input }) => {
      const { email, password, name } = input;
      
      const existingUser = await prisma.user.findUnique({ where: { email } });
      if (existingUser) throw new Error("该邮箱已注册");

      const hashedPwd = await bcrypt.hash(password, 10);
      await prisma.user.create({
        data: { email, name, hashedPassword: hashedPwd }
      });

      // 注册成功后自动登录
      await signIn("credentials", {
        email,
        password,
        redirect: false // 禁止自动跳转,前端自行处理
      });

      return { success: true };
    })
});

2. 确保NextAuth的CredentialsProvider配置正确

// server/auth.ts
import type { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { prisma } from "./db/client";
import bcrypt from "bcrypt";

export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      name: "账号密码登录",
      credentials: {
        email: { label: "邮箱", type: "email" },
        password: { label: "密码", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) return null;

        const user = await prisma.user.findUnique({ where: { email: credentials.email } });
        if (!user || !user.hashedPassword) return null;

        const isValidPwd = await bcrypt.compare(credentials.password, user.hashedPassword);
        return isValidPwd ? user : null;
      }
    })
  ],
  session: { strategy: "jwt" }
};

三、避坑总结

  • 别混合tRPC路由和普通API:tRPC路由必须走主入口pages/api/trpc/[trpc].ts,单独API文件别用tRPC Router
  • 密码必须加密:用bcrypt,绝对不能明文存数据库
  • 用tRPC的类型安全:用Zod做输入验证,避免前端传参错误
  • 注册后自动登录:结合NextAuth的signIn方法提升用户体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:35:24