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

NextJS+MongoDB构建Codepen类应用时触发TypeError: Cannot read properties of undefined (reading 'updatedRecord')问题求助

排查 Cannot read properties of undefined (reading 'updatedRecord') 错误

这个错误的核心原因很明确:你在解构 API 响应时,尝试读取 data.updatedRecord,但data是undefined。咱们一步步拆解问题,找到解决办法:

1. 先确认 API 实际返回的内容

旧教程的后端可能返回{ data: { updatedRecord, newRecordId } }这样的结构,但现在你的后端(不管是自己写的还是教程项目的)可能返回格式变了,或者请求本身就失败了。

先在save函数里加一行日志,看看真实的响应结构:

const save = async () => {
  setSaving(true);
  try {
    // ... 你的请求配置代码 ...
    const response = await fetch(`../api/pens/${id}`, requestOptions);
    // 新增:打印完整响应结果
    const rawResponse = await response.json();
    console.log("API 返回内容:", rawResponse);
    
    // 原来的解构代码先注释掉,先看日志
    // const { data: { updatedRecord, newRecordId }, } = rawResponse;
    // ... 后续代码 ...
  } catch (err) {
    console.error("保存出错:", err);
    setSaving(false);
  }
};

打开浏览器开发者工具的「控制台」和「网络」标签,点击保存按钮后:

  • 如果看到返回的是{ updatedRecord: ..., newRecordId: ... }(没有外层data),那直接把解构改成const { updatedRecord, newRecordId } = rawResponse就行;
  • 如果返回的是错误信息(比如{ error: "数据库连接失败" }),那说明后端逻辑有问题,需要去排查 API 路由的代码。

2. 检查请求方法和路由是否匹配

你代码里的请求方法逻辑有点反直觉:

method: !id ? "PUT" : "POST"

通常新建资源用POST,更新用PUT/PATCH,旧教程可能当时这么写没问题,但新版本的后端框架可能对方法的校验更严格。建议改成:

method: !id ? "POST" : "PUT"

另外,新建 pen 时id是undefined,这时候请求路径../api/pens/${id}会变成../api/pens/undefined,这大概率不是后端期望的路径。新建应该请求../api/pens,更新才带id:

const url = !id ? "../api/pens" : `../api/pens/${id}`;
const response = await fetch(url, requestOptions);

3. 给请求加上错误处理

如果后端返回了错误状态码(比如400、500),fetch不会自动抛出错误,会继续执行后续的解构逻辑,导致读取undefined的属性。给请求加上状态校验:

const response = await fetch(url, requestOptions);
if (!response.ok) {
  const errorData = await response.json().catch(() => ({}));
  throw new Error(errorData.message || `请求失败,状态码:${response.status}`);
}

4. 排查 Next.js API 路由的版本差异

几年前的 Next.js 用的是 Pages Router,API 路由放在pages/api目录下;如果你现在用的是 Next.js 13+ 的 App Router,API 路由的写法完全不同(放在app/api目录,导出POST/PUT等方法)。

如果教程用的是 Pages Router,而你用了 App Router,那需要把 API 路由改成对应版本的写法:

  • Pages Router 示例(pages/api/pens/[id].js):
export default async function handler(req, res) {
  if (req.method === "POST") {
    // 处理更新逻辑
  } else if (req.method === "PUT") {
    // 处理新建逻辑
  } else {
    res.status(405).json({ error error: "不支持的请求方法" });
  }
}
  • App Router 示例(app/api/pens/[id]/route.js):
export async function POST(request) {
  // 处理更新逻辑
}
export async function PUT(request) {
  // 处理新建逻辑
}

最后调整后的完整示例

把上面的修改整合起来,你的save函数可以改成这样:

const save = async () => {
  setSaving(true);
  try {
    const requestOptions = {
      method: !id ? "POST" : "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ html: htmlValue, css: cssValue, js: jsValue, id }),
    };
    const url = !id ? "../api/pens" : `../api/pens/${id}`;
    const response = await fetch(url, requestOptions);

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData.message || `请求失败,状态码:${response.status}`);
    }

    const rawResponse = await response.json();
    console.log("API 返回内容:", rawResponse);
    
    // 根据实际返回结构调整解构
    const { updatedRecord, newRecordId } = rawResponse.data || rawResponse;

    setSaving(false);
    if (!updatedRecord) {
      await router.push(`?id=${newRecordId}`);
    }
  } catch (error) {
    setSaving(false);
    console.error("保存失败:", error);
    // 这里可以加个用户提示,比如用toast告知保存出错
  }
};

内容的提问来源于stack exchange,提问作者Pepelius

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 15:17:32