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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:23:13