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

Node.js NFT API批量mint仅返回1份元数据问题排查求助

不需要额外添加循环函数或者请求延迟处理,问题根源是代码中的异步逻辑处理错误,具体修复方案和经验如下:

问题根因

  1. mint接口中updated.save()没有加await关键字
    Sequelize的save()是异步方法,当前写法不会等待数据库写入完成就直接返回响应,当智能合约并发调用多个mint请求时,多个请求同时查询到同一个未铸造的NFT记录,或者isMinted状态还没写入数据库就被下一个请求读取,导致只有第一个写入成功,其余都出现状态冲突。
  2. 批量调用/metadata/:id接口时,若NFT的mint操作还未完成数据库提交,就会触发Token not minted报错,导致后续元数据返回失败。
  3. create_nft接口里的forEach(async (item))写法存在逻辑漏洞,forEach不会等待异步回调执行完成,你返回响应的时候可能还有大量NFT没写入数据库。

修复方案

1. 修复mint接口的异步等待问题

mint: async (req, res, next) => {
  try {
    const { id } = req.params;
    const updated = await models.NFT.findByPk(id);

    if (!updated) {
      throw new Error("NFT ID invalid");
    }
    if (updated.isMinted) {
      throw new Error("NFT Already minted");
    }
    updated.isMinted = true;
    // 新增await等待数据库写入完成
    await updated.save();

    return res.json({
      data: "Token minted successfully",
      error: null,
      success: true,
    });
  } catch (error) {
    console.log("server error", error.message);
    next(error);
  }
}

2. 修复create_nft接口的异步等待问题

create_nft: async (req, res, next) => {
  try {
    const dir = path.resolve(__dirname + `../../../data/traitsfinal.json`);
    const readCards = fs.readFileSync(dir, "utf8");

    const parsed = JSON.parse(readCards);
    console.log("ya data ha final ??", parsed);
    // 替换forEach为Promise.all等待所有异步操作完成
    await Promise.all(parsed.map(async (item) => {
      await models.NFT.create({
        name: item.Name,
        description: item.Description,
        background: item.Background,
        body: item.Body,
        mouth: item.Mouth,
        eyes: item.Eyes,
        head_gear: item.Head_Gear,
        tokenId: item.tokenId,
        image: item.imagesIPFS,
      });
    }));

    return res.json({
      data: "nft created",
      error: null,
      success: true,
    });
  } catch (error) {
    console.log("server error", error.message);
    next(error);
  }
},

3. 可选优化:新增批量mint接口

如果智能合约需要批量铸造,可以新增批量mint接口,一次性处理多个NFT的铸造状态更新,减少并发请求冲突:

// controller新增方法
batch_mint: async (req, res, next) => {
  try {
    const { ids } = req.body; // 传入要铸造的NFT id数组
    if (!Array.isArray(ids) || ids.length === 0) {
      throw new Error("ids must be non-empty array");
    }
    // 开启事务保证所有铸造要么全部成功要么全部失败
    const transaction = await models.sequelize.transaction();
    try {
      for (const id of ids) {
        const nft = await models.NFT.findByPk(id, { transaction });
        if (!nft) throw new Error(`NFT ${id} invalid`);
        if (nft.isMinted) throw new Error(`NFT ${id} already minted`);
        nft.isMinted = true;
        await nft.save({ transaction });
      }
      await transaction.commit();
    } catch (err) {
      await transaction.rollback();
      throw err;
    }

    return res.json({
      data: "Batch mint successful",
      error: null,
      success: true,
    });
  } catch (error) {
    console.log("server error", error.message);
    next(error);
  }
}
// 路由新增
router.put("/batch-mint", batch_mint);

相关开发经验总结

  • 所有数据库读写操作只要返回Promise,必须加await等待执行完成,否则会出现脏读、写入丢失的问题
  • 并发修改同一张表的记录时,尽量用数据库事务保证操作的原子性,避免出现部分成功部分失败的不一致情况
  • 智能合约调用后端接口时,不要依赖接口的响应顺序,最好是先调用mint接口拿到成功响应后,再去请求元数据接口,避免出现元数据查询时铸造状态还未更新的问题
  • 不要在forEach、map等数组遍历方法里直接写不带等待的异步逻辑,要么用Promise.all等待所有执行完成,要么用for...of串行执行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 01:48:04