Skip to content

FirestoreConversationStore

FirestoreConversationStore is the production-grade Firestore implementation of ConversationStore. It provides collection prefixing, automatic Date/Timestamp conversion, and cascade deletion of messages when a conversation is deleted.

Import

import { FirestoreConversationStore } from "@modernpath/agent-framework";

Optional Dependency

The @google-cloud/firestore package is loaded lazily. It is only required when FirestoreConversationStore is instantiated. Consumers that use InMemoryConversationStore do not need the Firestore dependency.

Constructor

new FirestoreConversationStore(config?: FirestoreConversationStoreConfig)

FirestoreConversationStoreConfig

interface FirestoreConversationStoreConfig {
  projectId?: string;
  collectionPrefix?: string;
  firestore?: any;
}
Property Type Default Description
projectId string -- GCP project ID for Firestore client construction. Ignored if firestore is provided.
collectionPrefix string "" Prefix for collection names (e.g. "prod_" produces "prod_conversations").
firestore any -- Pre-constructed Firestore instance. Takes priority over projectId.

Firestore Collection Schema

{prefix}conversations/{conversationId}                          -- Conversation document
{prefix}conversations/{conversationId}/messages/{messageId}     -- Message subcollection

For the listConversations query with filters, create composite indexes on:

  • conversations: (userId, updatedAt DESC), (agentName, updatedAt DESC)

Implementation Details

Message Ordering

getMessages() returns messages ordered by timestamp ascending. When a limit is specified, the store queries in descending order with the limit and then reverses the results in memory. This ensures the most recent N messages are returned in the correct chronological order.

Cascade Deletion

deleteConversation() first deletes all messages in the subcollection (Firestore does not cascade subcollection deletes automatically), then deletes the conversation document.

Message Count

When addMessage() is called, it reads the current conversation document to get the count, increments it, and writes back. This is eventually consistent but sufficient for denormalized display counts.

Debugging

Enable debug logging by setting the DEBUG environment variable:

DEBUG=firestore-conversation-store node server.js

Error logs are always emitted regardless of the DEBUG setting.

Code Example

import { FirestoreConversationStore } from "@modernpath/agent-framework";

// Production setup
const conversationStore = new FirestoreConversationStore({
  projectId: "my-gcp-project",
  collectionPrefix: "prod_",
});

// Or with a shared Firestore instance
import { Firestore } from "@google-cloud/firestore";

const db = new Firestore({
  projectId: "my-gcp-project",
  ignoreUndefinedProperties: true,
});

const conversationStore2 = new FirestoreConversationStore({
  firestore: db,
  collectionPrefix: "staging_",
});

Multi-Environment Configuration

import {
  InMemoryConversationStore,
  FirestoreConversationStore,
  ConversationStore,
} from "@modernpath/agent-framework";

function createConversationStore(env: string): ConversationStore {
  if (env === "test") {
    return new InMemoryConversationStore();
  }

  return new FirestoreConversationStore({
    projectId: process.env.GCP_PROJECT_ID,
    collectionPrefix: `${env}_`,
  });
}

Usage with Conversation Handler

import {
  FirestoreConversationStore,
  createConversationHandler,
} from "@modernpath/agent-framework";

const conversationStore = new FirestoreConversationStore({
  projectId: "my-project",
  collectionPrefix: "prod_",
});

const handler = createConversationHandler({
  resolveAgent: (name) => agentFactory.resolve(name),
  getUserId: (event) => extractUserId(event.headers),
  conversationStore,
  defaultAgentName: "QAChatAgent",
  maxHistoryMessages: 50,
});