Next.js 14 API路由部署报错:default非合法导出,求Google登录用户订单查询方案
问题:部署Next.js应用时API路由类型错误,无法显示登录用户订单列表
问题背景
需要实现用户通过Google登录后,用Prisma从数据库查询该用户的订单并展示,但部署时出现TypeScript类型错误导致构建失败。
现有代码
API路由代码(src/app/api/order/route.ts)
import { NextApiRequest, NextApiResponse } from "next"; import prisma from "../../../../lib/prisma"; import { getSession } from "next-auth/react"; // Import getSession from next-auth/react export default async function (req: NextApiRequest, res: NextApiResponse) { try { const session = await getSession({ req }); // Get the session object from the request if (!session) { return res.status(401).json({ error: "Unauthorized" }); } if (req.method !== "GET") { return res.status(405).json({ error: "Method Not Allowed" }); } const userId = session.user?.email; // Get the user's email from the session if (!userId) { return res.status(400).json({ error: "User ID not found in session" }); } const orders = await prisma.post.findMany({ where: { author: { email: userId }, // Filter orders by user's email published: true, // Filter based on your criteria }, include: { author: { select: { name: true } }, }, }); res.status(200).json({ orders }); } catch (error) { console.error("Error fetching orders:", error); res.status(500).json({ error: "Internal Server Error" }); } }
页面组件代码(src/app/user-orders/page.tsx)
"use client" import { useState, useEffect } from "react"; import Post from "../ui/order-list"; // Update import path interface Order { id: string; title: string; content: string; authorName: string; } export default function Page() { const [orders, setOrders] = useState<Order[]>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { async function fetchOrders() { try { const response = await fetch("/api/order/route"); // Fetch orders from the API endpoint if (!response.ok) { throw new Error("Failed to fetch orders"); } const data = await response.json(); setOrders(data.orders); // Set orders fetched from the API setLoading(false); // Set loading state to false after fetching data } catch (error) { console.error("Error fetching orders:", error); setError("Failed to fetch orders. Please try again later."); // Set error state if fetching fails setLoading(false); // Set loading state to false after fetching data } } fetchOrders(); // Call the function to fetch orders when component mounts }, []); // Display loading message while fetching data if (loading) { return <div>Loading...</div>; } // Display error message if fetching orders fails if (error) { return <div>{error}</div>; } return ( <div className="container mx-auto mt-8 px-4 sm:px-0 text-gray-600"> <h1 className="text-2xl font-bold text-gray-900 mb-4">Your Orders</h1> {/* Display message if no orders found */} {orders.length === 0 ? ( <div>No orders found.</div> ) : ( // Display orders if available <div className="flex flex-wrap"> {orders.map((order) => ( <Post key={order.id} id={order.id} title={order.title} content={order.content} authorName={order.authorName || "Unknown"} /> ))} </div> )} </div> ); }
NextAuth配置代码(src/app/api/auth/[...nextauth]/route.ts)
import { PrismaClient } from "@prisma/client"; import NextAuth, { User as NextAuthUser } from "next-auth"; import GoogleProvider from "next-auth/providers/google"; interface MyUser extends NextAuthUser { email: string; name?: string; // Add other user-related fields as needed } const prisma = new PrismaClient(); const handler = NextAuth({ providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }), ], callbacks: { async signIn(params) { const { user } = params as { user: MyUser }; try { const existingUser = await prisma.user.findUnique({ where: { email: user.email }, }); if (!existingUser) { // If the user is not in the database, create a new user entry await prisma.user.create({ data: { email: user.email, name: user.name || "", // Ensure 'name' is a string, even if it's undefined // Add other user-related fields as needed }, }); } return true; // Continue the sign-in process } catch (error) { console.error("Error during sign-in:", error); return false; // Stop the sign-in process if an error occurs } }, }, }); export { handler as GET, handler as POST };
部署错误信息
▲ Next.js 14.0.4
- Environments: .env
Creating an optimized production build ...
✓ Compiled successfully
Linting and checking validity of types ...
Failed to compile.
src/app/api/orders/route.ts
Type error: Route "src/app/api/orders/route.ts" does not match the required types of a Next.js Route.
"default" is not a valid Route export field.
Error: Command "npx prisma generate && next build" exited with 1
修复方案
1. 修正API路由的导出方式与Session获取逻辑
Next.js 13+ App Router的API路由不支持export default,需导出对应HTTP方法的函数(如GET);同时获取Session要使用getServerSession而非getSession,且需要传入NextAuth的配置项。
修改后的src/app/api/order/route.ts:
import { getServerSession } from "next-auth/next"; import prisma from "../../../../lib/prisma"; import { authOptions } from "../auth/[...nextauth]/route"; export async function GET() { try { const session = await getServerSession(authOptions); if (!session) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 }); } const userEmail = session.user?.email; if (!userEmail) { return new Response(JSON.stringify({ error: "User email not found in session" }), { status: 400 }); } const orders = await prisma.post.findMany({ where: { author: { email: userEmail }, published: true, }, include: { author: { select: { name: true } }, }, }); // 转换数据结构匹配前端Order接口 const formattedOrders = orders.map(order => ({ id: order.id, title: order.title, content: order.content, authorName: order.author?.name || "Unknown" })); return new Response(JSON.stringify({ orders: formattedOrders }), { status: 200 }); } catch (error) { console.error("Error fetching orders:", error); return new Response(JSON.stringify({ error: "Internal Server Error" }), { status: 500 }); } }
2. 导出NextAuth配置项
在src/app/api/auth/[...nextauth]/route.ts中导出authOptions,供API路由调用:
import { PrismaClient } from "@prisma/client"; import NextAuth, { User as NextAuthUser } from "next-auth"; import GoogleProvider from "next-auth/providers/google"; interface MyUser extends NextAuthUser { email: string; name?: string; } const prisma = new PrismaClient(); // 导出authOptions export const authOptions = { providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }), ], callbacks: { async signIn(params) { const { user } = params as { user: MyUser }; try { const existingUser = await prisma.user.findUnique({ where: { email: user.email }, }); if (!existingUser) { await prisma.user.create({ data: { email: user.email, name: user.name || "", }, }); } return true; } catch (error) { console.error("Error during sign-in:", error); return false; } }, }, }; const handler = NextAuth(authOptions); export { handler as GET, handler as POST };
3. 修正前端API请求路径
前端fetch路径无需包含/route,Next.js会自动解析route.ts文件,修改src/app/user-orders/page.tsx中的fetch代码:
const response = await fetch("/api/order");
错误原因说明
- Next.js App Router的API路由规则与旧版Pages Router不同,要求导出明确的HTTP方法函数,而非默认导出;
getSession是Pages Router的Session获取方式,App Router需使用getServerSession并传入Auth配置;- 前端请求路径错误,App Router的API路由路径基于文件夹结构,无需指定
route.ts文件名。
内容的提问来源于stack exchange,提问作者Jayesh Kahnani
相关产品推荐
相关产品推荐

