Next.js 14 App Router调用本地Auth API遇404错误求助
问题:Next.js 13+ App Router API 404错误及Promise作用域问题
核心问题
- 调用注册API时持续返回404错误
- 消息监听器的Promise响应超出作用域
现有代码情况
1. API路由文件(app/api/auth/route.js)
import { hashPassword } from "@/app/lib/auth"; import { connectToDatabase } from "../../lib/db"; export async function POST(req, res) { const data = req.body; const { email, password } = data; if ( !email || !email.includes("@") || !password || password.trim().length < 7 ) { res.status(422).json({ message: "Invalid input - password should also be at least 7 characters long", }); } const client = await connectToDatabase(); const db = client.db(); const hashedPassword = await hashPassword(password); const result = await db.collection("users").insertOne({ email: email, password: hashedPassword, }); res.status(201).json({ message: "Created user!" }); client.close(); }
2. 前端LoginForm组件(components/LoginForm.js)
"use client"; // components/LoginForm.js import { useState, useRef } from "react"; import classes from "./login.module.css"; async function createUser(email, password) { const response = await fetch("http://localhost:3000/api/auth/route", { method: "POST", body: JSON.stringify({ email, password }), headers: { "Content-Type": "application/json", }, }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || "Something went wrong"); } return data; } const LoginForm = () => { const emailInputRef = useRef(); const passwordInputRef = useRef(); const [isLogin, setIsLogin] = useState(true); function switchAuthModeHandler() { setIsLogin((prevState) => !prevState); } async function submitHandler(event) { event.preventDefault(); const enteredEmail = emailInputRef.current.value; const enteredPassword = passwordInputRef.current.value; //optional: Add validation if (isLogin) { } else { try { const result = await createUser(enteredEmail, enteredPassword); console.log(result); } catch (error) { console.log(error); } } } return ( <section className={classes.auth}> <h1>{isLogin ? "Login" : "Sign Up"}</h1> <form onSubmit={submitHandler}> <div className={classes.control}> <label htmlFor='email'>Your Email</label> <input type='email' id='email' required ref={emailInputRef} /> </div> <div className={classes.control}> <label htmlFor='password'>Your Password</label> <input type='password' id='password' required ref={passwordInputRef} /> </div> <div className={classes.actions}> <button type='submit'>{isLogin ? "Login" : "Create Account"}</button> <button type='button' className={classes.toggle} onClick={switchAuthModeHandler} > {isLogin ? "Create new account" : "Login with existing account"} </button> </div> </form> </section> ); }; export default LoginForm;
3. 数据库连接函数(lib/db.js)
import { MongoClient } from "mongodb"; async function connectToDatabase() { const client = await MongoClient.connect( "mongodb+srv://<username>:<password>@cluster0.<string>.mongodb.net/" ); return client; }
问题修复步骤
1. 修正API路由路径
Next.js App Router中,app/api/auth/route.js对应的API路径是/api/auth,而非/api/auth/route。修改前端请求URL:
// LoginForm.js中的createUser函数 const response = await fetch("/api/auth", { // 去掉末尾的/route method: "POST", body: JSON.stringify({ email, password }), headers: { "Content-Type": "application/json", }, });
2. 修复API路由的Next.js 13+语法
App Router的API路由不再使用req, res参数,而是返回NextResponse:
// app/api/auth/route.js import { hashPassword } from "@/app/lib/auth"; import { connectToDatabase } from "../../lib/db"; import { NextResponse } from "next/server"; // 导入NextResponse export async function POST(req) { // 只接收req参数 const data = await req.json(); // 用req.json()替代req.body const { email, password } = data; if ( !email || !email.includes("@") || !password || password.trim().length < 7 ) { return NextResponse.json( // 返回NextResponse { message: "Invalid input - password should also be at least 7 characters long" }, { status: 422 } ); } const client = await connectToDatabase(); const db = client.db(); // 新增:检查用户是否已存在,避免重复创建 const existingUser = await db.collection("users").findOne({ email }); if (existingUser) { client.close(); return NextResponse.json( { message: "User already exists!" }, { status: 422 } ); } const hashedPassword = await hashPassword(password); const result = await db.collection("users").insertOne({ email: email, password: hashedPassword, }); client.close(); return NextResponse.json( // 返回NextResponse { message: "Created user!" }, { status: 201 } ); }
3. 解决Promise作用域问题
确保所有异步操作都正确等待,并且错误被捕获:
- API路由中,所有
await操作都正确使用,每个分支都有明确返回值(避免函数无返回导致的Promise异常) - 前端
submitHandler中,确保createUser的Promise被正确await,错误被catch处理
4. 数据库连接优化(可选)
为避免频繁创建数据库连接,建议使用连接池或缓存客户端:
// lib/db.js import { MongoClient } from "mongodb"; let client; let clientPromise; if (!process.env.MONGODB_URI) { throw new Error("Please add your Mongo URI to .env.local"); } const uri = process.env.MONGODB_URI; if (process.env.NODE_ENV === "development") { // 开发环境下缓存客户端 if (!global._mongoClientPromise) { client = new MongoClient(uri); global._mongoClientPromise = client.connect(); } clientPromise = global._mongoClientPromise; } else { // 生产环境直接创建连接 client = new MongoClient(uri); clientPromise = client.connect(); } export default clientPromise;
在API路由中修改使用方式:
import clientPromise from "../../lib/db"; // ... const client = await clientPromise; const db = client.db(); // ...
验证步骤
- 重启Next.js开发服务器
- 提交注册表单,检查网络请求是否返回201状态码
- 查看MongoDB数据库是否新增用户记录
内容的提问来源于stack exchange,提问作者Jason Rosenthal
相关产品推荐
相关产品推荐

