I am using the Google Firestore Emulator locally for development. However, when I query the database data, I find that the results are inconsistent and not correct each time. Here is the detailed information.
1.This is my firestore setup:
/**
* Firebase configuration for both local development and production
*/
import { initializeApp, getApps, FirebaseApp } from 'firebase/app';
import { getFirestore, connectFirestoreEmulator, Firestore } from 'firebase/firestore';
import { getAuth, connectAuthEmulator, Auth } from 'firebase/auth';
import logger from 'src/logger.ts';
// Firebase configuration
const firebaseConfig = {
// For local development with emulator, these can be dummy values
apiKey: process.env.FIREBASE_API_KEY || 'dummy-api-key',
authDomain: process.env.FIREBASE_AUTH_DOMAIN || 'dummy-project.firebaseapp.com',
projectId: process.env.FIREBASE_PROJECT_ID || 'dummy-project',
storageBucket: process.env.FIREBASE_STORAGE_BUCKET || 'dummy-project.appspot.com',
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID || '123456789',
appId: process.env.FIREBASE_APP_ID || 'dummy-app-id',
};
// Initialize Firebase app
let app: FirebaseApp;
if (getApps().length === 0) {
app = initializeApp(firebaseConfig);
logger.info('Firebase app initialized');
} else {
app = getApps()[0];
logger.info('Using existing Firebase app');
}
// Initialize Firestore
export const db: Firestore = getFirestore(app);
// Initialize Auth
export const auth: Auth = getAuth(app);
// Connect to emulators in development
if (process.env.NODE_ENV === 'development') {
try {
// Always connect to emulators in development
connectFirestoreEmulator(db, 'localhost', 8080);
logger.info('Connected to Firestore emulator on localhost:8080');
connectAuthEmulator(auth, 'http://localhost:9099');
logger.info('Connected to Auth emulator on localhost:9099');
} catch (error) {
logger.warn(`Could not connect to Firebase emulators: ${error}`);
logger.info('Make sure to start Firebase emulators with: firebase emulators:start');
}
}
export { app };
2.Then I start the Emulator by make firebase emulators:start --only firestore --project dummy-project
3.I do something and there are some items in my database.
item1: {...., expiredAt: {nanoseconds: 123000000, seconds: 1757341144}}
item2: {...., expiredAt: {nanoseconds: 76000000, seconds: 1757341146}}
item3: {...., expiredAt: {nanoseconds: 200000000, seconds: 1757341133}}
4.I try to get these 3 items by executing findUnexpiredThreads() function:
import {
Firestore,
collection,
doc,
setDoc,
getDoc,
getDocs,
updateDoc,
query,
where,
orderBy,
limit,
getCountFromServer,
} from 'firebase/firestore';
async findUnexpiredThreads(): Promise<IChatThread[]> {
try {
const q = query(
collection(this.db, this.threadsCollection),
where('expiresAt', '>', toFirestoreSafe(new Date()))
);
const snapshot = await getDocs(q);
return snapshot.docs.map(doc => fromFirestoreSafe(doc.data())) as IChatThread[];
} catch (error) {
logger.error(`Failed to find unexpired chat threads: ${error}`);
return [];
}
}
// Convert app data to Firestore-safe values: BigInt -> string, Date -> Timestamp
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function toFirestoreSafe<T = unknown>(value: T): any {
return deepMap(value as unknown as JsonValue, v => {
if (typeof v === 'bigint') {
return v.toString();
}
if (v instanceof Date) {
return Timestamp.fromDate(v);
}
return v;
});
}
// Convert Firestore data to app-friendly values: Timestamp -> Date
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function fromFirestoreSafe<T = unknown>(value: T): any {
return deepMap(value as unknown as JsonValue, v => {
if (v instanceof Timestamp) {
return v.toDate();
}
return v;
});
}
Result: Sometimes it returns 0 items, sometimes it returns 1/2/3 items, the result is unstable. In theory, it should always return 3 items, because the expiredAt of these 3 items is greater than now.
I'm not sure where the problem lies. I checked the contents of the database, and they haven't been modified. I was merely making queries, but I don't know why the results are incorrect, and the results of each query are also inconsistent.
1 Answer 1
I discovered that when the problem occurred, I stored expiredAt as a Map format (item1: {...., expiredAt: {nanoseconds: 123000000, seconds: 1757341144}}). I fixed it by not converting Date and timestamp types to maps when encountering them in toFirestoreSafe() and fromFirestoreSafe().
Comments
Explore related questions
See similar questions with these tags.
deepMapis. Maybe you could also simplify the code to only the minimum required that causes the problem.expiredAtas aMapformat (item1: {...., expiredAt: {nanoseconds: 123000000, seconds: 1757341144}}). I fixed it by not convertingDateandtimestamptypes to maps when encountering them intoFirestoreSafe()andfromFirestoreSafe(). Anyway, thank you for your time.