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

Express集成Prismic报错:undefined调用forEach触发TypeError

Prismic集成报错TypeError: Cannot read properties of undefined (reading 'forEach')修复

问题现象

从零搭建网站开展Prismic集成工作时,终端持续抛出TypeError: Cannot read properties of undefined (reading 'forEach')错误,本地运行的网站完全无法加载,涉及业务代码如下:

require('dotenv').config();

const fetch = require('node-fetch');
const path = require('path');
const express = require('express');

const app = express();
const port = process.env.PORT || 8005;

const Prismic = require('@prismicio/client');
const PrismicH = require('@prismicio/helpers');

// Initialize the prismic.io api
const initApi = (req) => {
  return Prismic.createClient(process.env.PRISMIC_ENDPOINT, {
    accessToken: process.env.PRISMIC_ACCESS_TOKEN,
    req,
    fetch,
  });
};

// Link Resolver
const HandleLinkResolver = (doc) => {
  // Define the url depending on the document type
  //   if (doc.type === 'page') {
  //     return '/page/' + doc.uid;
  //   } else if (doc.type === 'blog_post') {
  //     return '/blog/' + doc.uid;
  //   }

  // Default to homepage
  return '/';
};

// Middleware to inject prismic context
app.use((req, res, next) => {
  res.locals.ctx = {
    endpoint: process.env.PRISMIC_ENDPOINT,
    linkResolver: HandleLinkResolver,
  };
  res.locals.PrismicH = PrismicH;

  next();
});

app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));
app.locals.basedir = app.get('views');

const handleRequest = async (api) => {
  const [meta, home, about, { results: collection }] = await Promise.all([
    api.getSingle('meta'),
    api.getSingle('home'),
    api.getSingle('about'),
    api.query(Prismic.Predicates.at('document.type', 'collection'), {
      fetchLinks: 'product.image',
    }),
  ]);

  const assets = [];

  home.data.gallery.forEach((item) => {
    assets.push(item.image.url);
  });

  about.data.gallery.forEach((item) => {
    assets.push(item.image.url);
  });

  about.data.body.forEach((section) => {
    if (section.slice_type === 'gallery') {
      section.items.forEach((item) => {
        assets.push(item.image.url);
      });
    }
  });

  collection.forEach((collection) => {
    collection.data.products.forEach((item) => {
      assets.push(item.products_product.data.image.url);
    });
  });

  return {
    assets,
    meta,
    home,
    collections,
    about,
  };
};

app.get('/', async (req, res) => {
  const api = await initApi(req);
  const defaults = await handleRequest(api);

  res.render('pages/home', {
    ...defaults,
  });
});

app.get('/about', async (req, res) => {
  const api = await initApi(req);
  const defaults = await handleRequest(api);

  res.render('pages/about', {
    ...defaults,
  });
});

app.get('/collection', async (req, res) => {
  const api = await initApi(req);
  const defaults = await handleRequest(api);

  res.render('pages/collection', {
    ...defaults,
  });
});

app.get('/detail/:uid', async (req, res) => {
  const api = await initApi(req);
  const defaults = await handleRequest(api);

  const product = await api.getByUID('product', req.params.uid, {
    fetchLinks: 'collection.title',
  });

  res.render('pages/detail', {
    ...defaults,
    product,
  });
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

错误原因

报错核心是代码直接对可能为undefined的值调用forEach方法,具体触发场景有三类:

  • Prismic后台配置缺失:代码中getSingle拉取的meta/home/about单文档、query拉取的collection文档如果未在Prismic后台创建,或是文档下没有配置gallery/body/products等对应字段,返回值中对应属性就是undefined,直接调用forEach会直接抛错
  • 变量名拼写错误:handleRequest返回的对象中属性写为collections(复数),但实际存储集合数据的变量是collection(单数),后续模板渲染时如果取collections遍历也会触发同类错误
  • 无错误捕获逻辑:异步拉取Prismic数据的逻辑没有加catch兜底,接口请求失败、权限错误等场景下会直接导致进程崩溃

修复步骤

  1. 先核对Prismic后台配置

    • 确认已创建meta/home/about三个单类型文档,每个文档都添加了代码中用到的gallery、body字段并填充测试内容
    • 确认已创建collection、product类型的可重复文档,collection文档中添加了关联product的products字段
    • 确认.env文件中配置的PRISMIC_ENDPOINT、PRISMIC_ACCESS_TOKEN和后台信息一致
  2. 修改代码逻辑,添加空值兜底和错误处理
    将原有handleRequest函数替换为带兜底的版本,同时给路由加错误捕获:

const handleRequest = async (api) => {
  // 给Promise.all加catch,捕获接口请求错误
  const [meta, home, about, { results: collection = [] } = {}] = await Promise.all([
    api.getSingle('meta').catch(() => ({data: {}})),
    api.getSingle('home').catch(() => ({data: {}})),
    api.getSingle('about').catch(() => ({data: {}})),
    api.query(Prismic.Predicates.at('document.type', 'collection'), {
      fetchLinks: 'product.image',
    }).catch(() => ({results: []})),
  ]);

  const assets = [];

  // 所有遍历的字段加空数组兜底,可选链避免嵌套取值报错
  (home.data.gallery || []).forEach((item) => {
    if(item?.image?.url) assets.push(item.image.url);
  });

  (about.data.gallery || []).forEach((item) => {
    if(item?.image?.url) assets.push(item.image.url);
  });

  (about.data.body || []).forEach((section) => {
    if (section.slice_type === 'gallery') {
      (section.items || []).forEach((item) => {
        if(item?.image?.url) assets.push(item.image.url);
      });
    }
  });

  (collection || []).forEach((colItem) => {
    // 调整循环内变量名,和外层collection数组区分,避免变量遮蔽
    (colItem.data.products || []).forEach((item) => {
      if(item?.products_product?.data?.image?.url) {
        assets.push(item.products_product.data.image.url);
      }
    });
  });

  return {
    assets,
    meta,
    home,
    collection, // 修复之前的拼写错误,和变量名统一
    about,
  };
};

// 给所有路由加错误兜底,以首页为例,其他路由按相同逻辑修改
app.get('/', async (req, res, next) => {
  try {
    const api = await initApi(req);
    const defaults = await handleRequest(api);
    res.render('pages/home', {
      ...defaults,
    });
  } catch (err) {
    next(err);
  }
});

修改后重启服务,只要Prismic后台配置正确,就不会再出现forEach相关的报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 08:33:11