Next.js 13(App目录)调用内置API报错:无效URL问题求助
解决Next.js 13 App Router中调用Pages API路由的路径错误问题
问题原因
你在App Router的服务器组件(src/app/page.tsx默认是服务器组件)中使用相对路径/api/products调用fetch时,服务器端执行环境没有当前域名的上下文,无法自动补全base URL,导致解析路径失败。而浏览器客户端环境会自动用当前页面域名补全相对路径,所以用完整URL能正常工作。
三种可行解决方案
方案1:将fetch逻辑移到客户端组件
在客户端组件中使用相对路径调用API,浏览器会自动处理路径补全:
// src/app/ProductsClient.tsx 'use client'; import { useEffect, useState } from 'react'; interface Product { id: number; name: string; // 补充你的产品字段 } const ProductsClient = () => { const [products, setProducts] = useState<Product[]>([]); useEffect(() => { const getProducts = async () => { try { const res = await fetch('/api/products'); const data = await res.json(); setProducts(data); } catch (err) { console.error(err); } }; getProducts(); }, []); return ( <div> {products.map(product => ( <div key={product.id}>{product.name}</div> ))} </div> ); }; export default ProductsClient;
然后在page.tsx中引入该组件:
// src/app/page.tsx import ProductsClient from './ProductsClient'; export default function Home() { return ( <main> <h1>产品列表</h1> <ProductsClient /> </main> ); }
方案2:服务器组件中使用绝对路径
通过环境变量配置base URL,在fetch时拼接成绝对路径:
- 在项目根目录创建
.env文件:
NEXT_PUBLIC_APP_URL=http://localhost:3000
生产环境替换为你的实际域名。
- 修改
page.tsx中的fetch逻辑:
// src/app/page.tsx interface Product { id: number; name: string; // 补充你的产品字段 } const getProducts = async () => { try { const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'; const res = await fetch(`${baseUrl}/api/products`); const data = await res.json(); return data as Product[]; } catch (err) { console.error(err); return []; } }; export default async function Home() { const products = await getProducts(); return ( <main> <h1>产品列表</h1> {products.map(product => ( <div key={product.id}>{product.name}</div> ))} </main> ); }
方案3:直接复用API路由的业务逻辑(推荐)
服务器组件可以直接运行服务器端代码,无需通过HTTP请求调用API,直接导入数据或逻辑:
- 抽离产品数据到单独文件:
// src/lib/products.ts export interface Product { id: number; name: string; // 补充你的产品字段 } export const products: Product[] = [ { id: 1, name: "产品1" }, { id: 2, name: "产品2" }, // 你的产品数据 ];
- 在
page.tsx中直接使用:
// src/app/page.tsx import { products } from '@/lib/products'; export default function Home() { return ( <main> <h1>产品列表</h1> {products.map(product => ( <div key={product.id}>{product.name}</div> ))} </main> ); }
内容的提问来源于stack exchange,提问作者im-mahadi
相关产品推荐
相关产品推荐

