问题背景
换了 Next.js 框架后,代码部署到 Vercel:
- 代码放在
pages/api/like.ts - 访问
https://xxx.com/api/like - 结果:Error: Cannot GET /api/like
Vercel 根本没识别出这是一个 Next.js 项目,而是把整个项目当成了静态文件处理。
核心结论
当 Vercel 检测为
None(框架未知)时,默认会将项目当作”纯静态站点”处理。这意味着所有 API 路由都不会被启用,直接导致 404 错误。首次部署或项目结构不标准时,Vercel 可能无法自动检测出 Next.js 框架,此时需要手动创建
vercel.json文件指定framework为'nextjs'。
我的真实经历
项目背景
试着把项目从 Node.js 迁移到 Next.js,想在 Vercel 上跑起来。
原来 Node.js 的结构:
blog-like-api/├── api/│ └── like.ts├── page/│ └── index.ts└── package.json迁移到 Next.js 后变成了:
blog-like-api/├── pages/│ ├── api/│ │ └── like.ts → /api/like│ └── index.ts → /├── package.json└── ...但在 Vercel 上跑不通 —— Vercel 检测不出这是 Next.js,所有文件都当静态文件处理,API 路由直接失效。加了 vercel.json 指定框架后问题才解决。
换了 Next.js 后的遭遇
按照 Next.js 的约定式路由,pages/api/like.ts 应该映射到 /api/like。
但 Vercel 返回:
Cannot GET /api/likeVercel 没有识别出这是 Next.js 项目。
解决方案:指定 framework
需要创建 vercel.json 强制指定框架:
{ "framework": "nextjs"}加上这个配置后,Vercel 才正确识别 Next.js 框架,API 路由才正常工作。
Vercel 的框架检测机制
自动检测原理
Vercel 部署时会自动检测项目使用的框架,检测依据包括:
| 检测依据 | 说明 |
|---|---|
package.json 的 dependencies | 检测是否安装了特定框架的包 |
| 特定文件夹结构 | 如 pages/、app/、src/ 等 |
| 配置文件 | 如 next.config.js、nuxt.config.ts 等 |
| 构建脚本 | 如 "build": "next build" |
检测失败的后果
当 Vercel 检测为 None(框架未知)时:
Vercel 会默认将项目当作”纯静态站点”处理。此时 Next.js 的 API 路由功能不会被启用,所有
pages/api/*或app/api/*的文件都不会被当作 API 端点。
具体来说:
pages/api/like.ts这类 API 文件,在部署后不会被编译成 Serverless Function- 相反,它们会被当作普通的静态资源文件处理
- 访问
/api/like时,Vercel 找不到对应的端点,返回 404 或Cannot GET错误
这就是为什么即使你正确放置了
pages/api/like.ts,访问/api/like依然会返回 404。
可能误判的原因
| 原因 | 说明 |
|---|---|
| 混合多种框架 | package.json 同时有 React 和 Vue 依赖,Vercel 无法判断主框架 |
| 自定义结构 | 没有使用框架的默认文件夹结构 |
| 缺少关键依赖 | 依赖在 devDependencies 而非 dependencies |
| 首次部署检测失败 | Vercel 首次部署时的误判 |
| 没有配置文件 | 缺少 next.config.js 等框架配置 |
误判案例:混合框架依赖
假设 package.json 是这样:
{ "dependencies": { "react": "^18.0.0", "vue": "^3.0.0", "next": "^14.0.0" }}这种情况下,Vercel 可能无法判断主框架是哪个。虽然项目实际用的是 Next.js(React 生态),但同时存在 Vue 依赖会增加检测的不确定性。
何时需要 vercel.json,何时可以依赖自动检测
| 情况 | 推荐 |
|---|---|
结构标准的 Next.js 项目(包含 next.config.js、pages/ 等) | 通常能自动检测,无需 vercel.json |
| 从其他框架迁移的项目 | 建议添加 vercel.json |
| 项目结构特殊或使用自定义文件夹 | 建议添加 vercel.json |
| 首次部署失败后 | 必须添加 vercel.json |
如何确认是否检测正确
查看 Vercel 构建日志。在 Vercel 控制台:
- 进入 Deployments 页面
- 选择对应的部署
- 点击对应的部署进入详情页
- 在 Logs 标签下查看构建日志
日志中会显示框架检测结果:
[INFO] Detected Framework: Next.js ← 正确识别[INFO] Detected Framework: None ← 检测失败,需要手动指定强制指定框架
vercel.json 配置
创建 vercel.json 文件明确指定框架:
{ "framework": "nextjs"}支持的框架值
| framework 值 | 框架 | 触发效果 |
|---|---|---|
"nextjs" | Next.js | 启用 Next.js 构建流程和 API 路由 |
"nuxtjs" | Nuxt.js | 启用 Nuxt 构建流程和 API 路由 |
"react" | Create React App | 启用 React 构建流程 |
"vue" | Vue | 启用 Vue 构建流程 |
"svelte" | Svelte | 启用 Svelte 构建流程 |
"angular" | Angular | 启用 Angular 构建流程 |
"static" | 纯静态站点 | 彻底禁用所有 API 路由,所有文件当静态资源处理 |
"nodejs" | Node.js 原生项目 | 启用 Node.js 构建流程 |
重要:选择正确的
framework值至关重要。不同的框架值会触发 Vercel 完全不同的构建流程和路由规则。错误选择的风险:如果错误地将
"framework"设置为"static",即使你的文件结构完全正确,Next.js 的 API 路由也会被彻底禁用。这是因为"static"会告诉 Vercel 这是一个纯静态站点,不会启动任何 API 路由编译流程。这些值是 Vercel 官方文档中定义的标准标识符,具体支持情况请以 Vercel 官方文档 为准。
vercel.json 完整配置示例
{ "framework": "nextjs", "buildCommand": "npm run build", "devCommand": "npm run dev", "installCommand": "npm install"}常见错误与解决方案
错误一:Vercel 没识别出框架
❌ Cannot GET /api/like✅ 添加 vercel.json 指定 framework解决:
{ "framework": "nextjs"}错误二:Pages 和 App 路由混用
❌ pages/api/like/route.ts(不存在的结构)✅ pages/api/like.ts(Pages 路由)✅ app/api/like/route.ts(App 路由)错误三:以为是 Express 的自定义路由
常见认知误区:许多从 Express 转来的开发者,习惯了代码定义路由,容易误以为 Next.js 也可以通过配置自定义路由路径。
实际上,Next.js 的约定式路由是固定的、不可自定义的。你不能把 pages/api/like.ts 映射到 /custom/like,也不能把 app/api/users.ts 映射到 /api/v1/users。
如果你习惯了 Express 中 app.get('/path', handler) 的写法,你可能会误以为 Next.js 也能自定义路径——但这是不成立的。
❌ 期望 pages/api/like.ts 映射到 /custom/like✅ Next.js 不能自定义路由路径错误四:buildCommand 不正确
{ "framework": "nextjs", "buildCommand": "npm run build", "devCommand": "npm run dev", "installCommand": "npm install"}Next.js 的约定式路由
前提:只有当 Vercel 正确识别 Next.js 框架后,才会应用下面的约定式路由规则。如果框架检测失败,无论文件放在
pages/api还是app/api,都不会被识别为 API 路由。
Pages 路由(pages/ 目录)
这是 Next.js 传统的路由方式:
文件系统 → URL 路径pages/ ├── index.ts → / ├── about.ts → /about ├── contact.ts → /contact ├── api/ │ ├── like.ts → /api/like │ ├── users.ts → /api/users │ └── users/ │ └── index.ts → /api/users └── posts/ ├── index.ts → /posts └── [slug].ts → /posts/123(动态路由)App 路由(app/ 目录,Next.js 13+)
这是新版路由方式,使用 React Server Components:
文件系统 → URL 路径app/ ├── page.tsx → / ├── about/ │ └── page.tsx → /about ├── contact/ │ └── page.tsx → /contact ├── api/ │ └── like/ │ └── route.ts → /api/like └── posts/ ├── page.tsx → /posts └── [slug]/ └── page.tsx → /posts/123(动态路由)注意:App 路由中每个页面文件必须命名为
page.tsx(或page.js),不能使用index.tsx。这是 Next.js 官方约定的命名规范。
Pages vs App 路由对比
| 特性 | Pages 路由 | App 路由 |
|---|---|---|
| 目录 | pages/ | app/ |
| API 文件 | pages/api/*.ts | app/api/*/route.ts |
| 默认导出 | 函数组件 | 函数组件 |
| 数据获取 | getServerSideProps | Server Components 中直接异步获取 |
| 布局 | _app.tsx | layout.tsx |
| 状态管理 | React 原生状态及第三方库 | React 原生状态及第三方库(需在客户端组件中使用) |
常见错误:文件放错位置
重要提醒:即使框架检测正确,文件放错位置同样会导致 404。
❌ 将 API 文件放在 public/ 目录下 public/api/like.ts → 不会映射到 /api/like
❌ 将 API 文件放在根目录的任意位置 api/like.ts → 不会映射到 /api/like
✅ Pages 路由:pages/api/*.ts✅ App 路由:app/api/*/route.tsNext.js 的约定式路由只认 pages/api/ 和 app/api/ 这两个固定目录,其他位置的 API 文件不会被处理。
不同平台的路由映射规则
对比一览
| 平台/框架 | 路由定义方式 | API 文件位置 | API URL |
|---|---|---|---|
| Express/Koa | 代码定义 | 任意(在代码里指定) | 代码里写 /api 就是 /api |
| Next.js (Pages) | 约定式 | pages/api/*.ts | 文件路径自动映射 |
| Next.js (App) | 约定式 | app/api/*/route.ts | 文件路径自动映射 |
| Nuxt 3 | 约定式 | server/api/*.ts | 文件路径自动映射 |
| Gatsby | 约定式 | src/pages/*.js | 文件路径自动映射 |
| 纯静态 | 文件结构 | public/* | 与 public 下结构相同 |
核心洞察:使用约定式路由的框架(如 Next.js)将路由逻辑与文件系统强绑定,而代码定义路由(如 Express)则允许在代码中自由定义路径。理解这一点是避免路径混淆的关键。
核心区别
代码定义路由(如 Express)给予开发者完全的路径控制权,而约定式路由(如 Next.js)则通过文件系统结构隐式定义路径,牺牲了灵活性以换取开发效率和一致性。
Node.js/Express(代码定义路由):
目录名和路由没有直接关系。page/index.js 不等于 / 路由,只是在代码里挂载为 /:
blog-like-api/├── page/│ └── index.js ← 只是文件名叫 index,不是 /├── api/│ └── like.js ← 只是文件名叫 like,不是 /api/like└── app.jsconst pageIndex = require('./page/index');const apiLike = require('./api/like');
app.use('/', pageIndex); // page/index.js 里的 get('/') 才映射到 /app.use('/api', apiLike); // api/like.js 里的 get('/') 才映射到 /apiNext.js(约定式路由):
文件名直接决定 URL,pages/ 是约定俗成的目录名称(不是固定的,你可以用 app/):
pages/├── index.ts → /├── about.ts → /about└── api/ └── like.ts → /api/like关键差异:Express 里你可以把 page/index.js 挂载到 /hello,api/like.js 挂载到 /world,完全自由。而 Next.js 不行,pages/api/like.ts 只能映射到 /api/like,不能自定义。
调试步骤
第一步:检查 Vercel 构建日志
查看是否正确识别了框架。在 Vercel 控制台进入 Deployments → 选择部署 → 在 Logs 标签下查看构建日志:
[INFO] Detected Framework: Next.js ← 正确[INFO] Detected Framework: None ← 问题在这里这直接印证了核心结论中的判断:如果检测为
None,说明 Vercel 没有识别出框架,项目被当作纯静态处理。
第二步:添加 vercel.json
{ "framework": "nextjs"}第三步:检查路由文件位置
| 路由方式 | 正确位置 |
|---|---|
| Pages 路由 | pages/api/*.ts |
| App 路由 | app/api/*/route.ts |
第四步:本地测试
# 安装 Vercel CLInpm i -g vercel
# 本地运行vercel dev# 通常显示 http://localhost:3000
# 本地运行后:# pages/api/like.ts → http://localhost:3000/api/like重要:本地测试无法验证 Vercel 的框架检测机制。
vercel dev主要验证路由逻辑和代码正确性,但它无法模拟 Vercel 的自动检测流程。
本地测试的验证范围:
| 能验证 | 不能验证 |
|---|---|
| API 路由逻辑正确性 | Vercel 的框架检测机制 |
| 函数代码执行正确性 | Vercel 特定的构建流程 |
| 本地开发环境运行 | 边缘函数或 Serverless Functions 的线上行为 |
| 环境变量的线上配置 |
第五步:健康检查接口
添加健康检查接口,用于验证部署结果:
export default function handler(req, res) { res.status(200).json({ message: "API 正常", url: req.url, method: req.method, timestamp: new Date().toISOString() });}用途:在添加 vercel.json 并重新部署后,立即访问 /api/test 验证 API 路由是否已恢复正常。
健康检查接口的多重用途:
- 验证部署和路由:确认 API 是否被正确部署和路由
- 前端健康探测:作为前端应用检测后端服务状态的探测点
- CI/CD 流程集成:在持续集成/部署流程中作为健康检查端点
预防措施
预防胜于治疗。在初始化 Next.js 项目时,就提前创建
vercel.json文件,并将其纳入版本控制,可以从根源上避免此类问题。
推荐做法
-
使用
create-next-app创建项目后,立即添加vercel.jsonnpx create-next-app my-blog --typescriptcd my-blog# 创建 vercel.jsonecho '{"framework": "nextjs"}' > vercel.json -
将其纳入版本控制
确保团队成员和 CI/CD 环境使用相同的配置:
git add vercel.jsongit commit -m "chore: add vercel.json with explicit framework" -
在项目文档中记录
如果项目有 README,建议在其中记录
vercel.json的作用,避免其他协作者误删。
总结
核心要点
- 换了框架后,Vercel 可能检测不出来 — 需要
vercel.json强制指定 - 检测为 None 时,项目被当作纯静态处理 — API 文件不会被编译成 Serverless Function
- Next.js 用约定式路由 — 文件位置决定 URL,不能自定义
- Pages 和 App 路由结构不同 — 不要混用
- 本地测试无法验证框架检测 — 必须以 Vercel 构建日志为准
一句话记住
Vercel 不一定能猜对框架。当出现”Cannot GET”时,先检查 Vercel 有没有正确识别你的框架。
常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| API 返回 404 | Vercel 未识别框架 | 添加 vercel.json 指定 "framework": "nextjs" |
| 框架检测为 None | 项目结构特殊或缺少依赖 | 检查 package.json 依赖和文件夹结构,必要时添加 vercel.json |
| API 文件位置正确但无效 | 文件放在 public/ 或其他非约定目录 | 确认文件在 pages/api/ 或 app/api/ 目录下 |
| 框架值设为 static 后 API 全挂 | 误将 framework 设置为 “static” | 更改为 "framework": "nextjs" |
快速修复清单
| 问题 | 解决方案 |
|---|---|
| Vercel 没识别框架 | 添加 vercel.json 指定 framework |
| Pages 路由不工作 | 确认文件在 pages/api/*.ts |
| App 路由不工作 | 确认文件在 app/api/*/route.ts |
| 本地测试 | vercel dev |
读者行动清单
-
检查项目结构:确认是否使用了标准的 Next.js 项目结构(包含
next.config.js、pages/或app/) -
查看 Vercel 构建日志:确认框架检测结果是否为
None -
如果检测失败:在项目根目录创建
vercel.json,添加"framework": "nextjs" -
确认 API 文件位置:Pages 路由放在
pages/api/*.ts,App 路由放在app/api/*/route.ts -
本地测试:使用
vercel dev验证路由逻辑 -
重新部署:触发新的部署流程
(重要) 添加
vercel.json后,必须触发一次新的部署(例如git push),否则配置不会生效。 -
验证 API 端点:通过健康检查接口
/api/test或实际端点/api/like确认正常工作
vercel.json 最小配置
{ "framework": "nextjs"}EOF
部分信息可能已经过时









