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

Remix+Shopify Hydrogen中Firestore getDocs查询无文档返回问题

问题:Firebase Firestore在Shopify Hydrogen(Remix)中读取不到文档

调用getDocs(collection(db, "products"))返回空快照,已确认以下信息:

  • 连接正常,读取请求已发送至Firestore
  • Firebase配置信息正确
  • Firestore的products集合存在有效文档
  • 安全规则处于测试模式(允许所有读写操作)

但日志显示querySnapshot的docs和oldDocs属性的root对象size均为0,fromCache为true,无报错信息,怀疑是区域或Remix环境问题,寻求排查方向与解决方案。


排查方向与解决方案

1. 强制从服务器读取,绕过缓存

fromCache: true说明当前读取的是本地缓存,但缓存中无数据。可以在查询时强制指定从服务器拉取:

// 修改业务代码中的getDocs调用
const querySnapshot = await getDocs(collection(db, 'products'), {
  source: 'server'
});

2. 显式指定Firestore数据库区域

若Firestore数据库设置为北美区域,初始化时未指定区域可能导致跨区域连接异常。修改Firebase配置文件,显式指定区域:

// firebase-config.js
import {initializeApp} from 'firebase/app';
import {getFirestore} from 'firebase/firestore';

const firebaseConfig = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.FIREBASE_APP_ID,
  measurementId: process.env.FIREBASE_MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
// 显式指定数据库区域,北美区域示例为"us-central1"
db.settings({ location: 'us-central1' });

export default db;

3. 验证服务器端环境变量与访问权限

Remix的loader默认在服务器端执行,需确认:

  • 服务器端环境变量已正确加载:在loader中打印console.log(process.env.FIREBASE_PROJECT_ID)验证
  • 服务器网络可访问Firestore:编写独立Node.js脚本测试读取,排除Remix环境干扰:
// test-firestore.js
import { initializeApp } from 'firebase/app';
import { getFirestore, collection, getDocs } from 'firebase/firestore';
import dotenv from 'dotenv';
dotenv.config();

const firebaseConfig = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID,
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

async function testRead() {
  const snap = await getDocs(collection(db, 'products'));
  console.log('文档数量:', snap.size);
  snap.forEach(doc => console.log('文档数据:', doc.data()));
}
testRead();

4. 检查集合名称大小写一致性

Firestore集合名称大小写敏感,确认代码中collection(db, "products")的名称与Firestore控制台中的集合名称完全一致(避免"Products"与"products"这类差异)。

5. 启用调试日志排查请求细节

开启Firestore调试日志,查看请求的详细过程与潜在错误:

// 在firebase-config.js中添加
import { setLogLevel } from 'firebase/firestore';
setLogLevel('debug');

附用户提供的相关代码

Firebase配置文件

import {initializeApp} from 'firebase/app';
import {getFirestore} from 'firebase/firestore';

const firebaseConfig = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.FIREBASE_APP_ID,
  measurementId: process.env.FIREBASE_MEASUREMENT_ID,
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

export default db;

Remix业务代码

import db from '~/firebase-config';
import {collection, getDocs, query} from 'firebase/firestore';
...

export async function loader(args) {
const criticalData = await loadCriticalData(args);

  return defer({...criticalData});
}

async function loadCriticalData({context, params, request}) {
  const {handle} = params; 
  const {storefront} = context;

  const products = []; // products array to store collection data

  if (!handle) {
    throw new Response(null, {status: 404});
  }

  const [{product}, variants, querySnapshot] = await Promise.all([
    context.storefront.query(PRODUCT_QUERY, {
      variables: {
        handle,
        selectedOptions: getSelectedProductOptions(request),
      },
    }),
    context.storefront.query(VARIANTS_QUERY, {
      variables: {handle: params.handle},
    }),
    getDocs(collection(db, 'products')),
  ]);

// organize products collection array
  console.log('querySnap:', JSON.stringify(querySnapshot));
  querySnapshot.forEach((doc) => {
    products.push(doc.data());
  });
  return {
    product,
    variants,
    products,
  };
}

querySnapshot日志示例

"_firestore": {
    "app": { ... },
    "databaseId": { "projectId": "...", "database": "(default)" },
    "settings": { ... }
  },
  "_snapshot": {
    "query": { ... },
    "docs": { "keyedMap": { "root": { "size": 0 } }, "sortedSet": { "root": { "size": 0 } } },
    "oldDocs": { "keyedMap": { "root": { "size": 0 } }, "sortedSet": { "root": { "size": 0 } } },
    "docChanges": [],
    "mutatedKeys": { "data": { "root": { "size": 0 } } },
    "fromCache": true,
    "syncStateChanged": true,
    "excludesMetadataChanges": false,
    "hasCachedResults": false
  },
  "metadata": { "hasPendingWrites": false, "fromCache": true },
  "query": { "converter": null, "_query": { "path": { "segments": ["products"], "offset": 0, "len": 1 } } },
  "type": "collection",
  "firestore": { ... }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:14:53