or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

collections-queries.mdcore-database.mddata-snapshots.mddata-types.mddocument-references.mdindex.mdtransactions-batches.md
tile.json

data-snapshots.mddocs/

Data Snapshots

Types for document and query snapshots that represent data retrieved from Firestore, including metadata and change tracking.

Capabilities

DocumentSnapshot Class

Snapshot of a Firestore document that may or may not exist.

class DocumentSnapshot<T = DocumentData, T2 extends DocumentData = DocumentData> {
  protected constructor();
  
  /** Whether the document exists */
  readonly exists: boolean;
  
  /** Reference to the document */
  readonly ref: DocumentReference<T>;
  
  /** Document ID */
  readonly id: string;
  
  /** Snapshot metadata */
  readonly metadata: SnapshotMetadata;
  
  /** Get document data, undefined if document doesn't exist */
  data(options?: SnapshotOptions): T | undefined;
  
  /** Get a specific field value */
  get(fieldPath: string | FieldPath, options?: SnapshotOptions): any;
  
  /** Check if this snapshot equals another */
  isEqual(other: DocumentSnapshot<T>): boolean;
}

QueryDocumentSnapshot Class

Snapshot of a document that is guaranteed to exist.

class QueryDocumentSnapshot<T = DocumentData, T2 extends DocumentData = DocumentData> extends DocumentSnapshot<T, T2> {
  private constructor();
  
  /** Get document data, guaranteed to return data (never undefined) */
  data(options?: SnapshotOptions): T;
}

QuerySnapshot Class

Result of a Firestore query containing multiple documents.

class QuerySnapshot<T = DocumentData, T2 extends DocumentData = DocumentData> {
  private constructor();
  
  /** Query that produced this snapshot */
  readonly query: Query<T>;
  
  /** Snapshot metadata */
  readonly metadata: SnapshotMetadata;
  
  /** Array of document snapshots */
  readonly docs: Array<QueryDocumentSnapshot<T>>;
  
  /** Number of documents in the snapshot */
  readonly size: number;
  
  /** Whether the snapshot is empty */
  readonly empty: boolean;
  
  /** Get document changes since last snapshot */
  docChanges(options?: SnapshotListenOptions): Array<DocumentChange<T>>;
  
  /** Iterate over all documents */
  forEach(
    callback: (result: QueryDocumentSnapshot<T>) => void,
    thisArg?: any
  ): void;
  
  /** Check if this snapshot equals another */
  isEqual(other: QuerySnapshot<T>): boolean;
}

Document Changes

Types for tracking changes in query results.

type DocumentChangeType = 'added' | 'removed' | 'modified';

interface DocumentChange<T = DocumentData, T2 extends DocumentData = DocumentData> {
  /** Type of change */
  readonly type: DocumentChangeType;
  
  /** Document that changed */
  readonly doc: QueryDocumentSnapshot<T>;
  
  /** Previous index in the snapshot (-1 for 'added') */
  readonly oldIndex: number;
  
  /** New index in the snapshot (-1 for 'removed') */
  readonly newIndex: number;
}

Snapshot Metadata

Information about how a snapshot was obtained.

interface SnapshotMetadata {
  /** Whether the snapshot has pending writes */
  readonly hasPendingWrites: boolean;
  
  /** Whether the snapshot came from cache */
  readonly fromCache: boolean;
  
  /** Check if this metadata equals another */
  isEqual(other: SnapshotMetadata): boolean;
}

Snapshot Options

Configuration for reading snapshot data.

interface SnapshotOptions {
  /** How to handle server timestamps during reads */
  readonly serverTimestamps?: 'estimate' | 'previous' | 'none';
}

interface SnapshotListenOptions {
  /** Include metadata-only changes in listeners */
  readonly includeMetadataChanges?: boolean;
}

Usage Examples

Document Snapshot Handling

import type { DocumentSnapshot, SnapshotOptions } from "@firebase/firestore-types";

interface User {
  name: string;
  email: string;
  lastLogin?: Timestamp;
}

function handleDocumentSnapshot(snapshot: DocumentSnapshot<User>) {
  console.log('Document ID:', snapshot.id);
  console.log('Document exists:', snapshot.exists);
  console.log('From cache:', snapshot.metadata.fromCache);
  console.log('Has pending writes:', snapshot.metadata.hasPendingWrites);
  
  if (snapshot.exists) {
    // Get all data
    const userData = snapshot.data();
    console.log('User name:', userData?.name);
    
    // Get specific field
    const email = snapshot.get('email');
    console.log('Email:', email);
    
    // Handle server timestamps
    const options: SnapshotOptions = { serverTimestamps: 'estimate' };
    const dataWithEstimates = snapshot.data(options);
    console.log('Last login (estimated):', dataWithEstimates?.lastLogin);
  }
}

Query Snapshot Processing

import type { QuerySnapshot, QueryDocumentSnapshot } from "@firebase/firestore-types";

function processQuerySnapshot(snapshot: QuerySnapshot<User>) {
  console.log(`Found ${snapshot.size} users`);
  console.log('Query is empty:', snapshot.empty);
  console.log('From cache:', snapshot.metadata.fromCache);
  
  // Process all documents
  snapshot.forEach((doc: QueryDocumentSnapshot<User>) => {
    const user = doc.data(); // Guaranteed to exist
    console.log(`User ${doc.id}: ${user.name} (${user.email})`);
  });
  
  // Or use the docs array directly
  const users = snapshot.docs.map(doc => ({
    id: doc.id,
    ...doc.data()
  }));
  
  return users;
}

Change Tracking

import type { DocumentChange, DocumentChangeType } from "@firebase/firestore-types";

function handleQueryChanges(snapshot: QuerySnapshot<User>) {
  const changes = snapshot.docChanges();
  
  changes.forEach((change: DocumentChange<User>) => {
    const user = change.doc.data();
    
    switch (change.type) {
      case 'added':
        console.log(`New user added at index ${change.newIndex}:`);
        console.log(`  ${user.name} (${user.email})`);
        break;
        
      case 'modified':
        console.log(`User modified, moved from ${change.oldIndex} to ${change.newIndex}:`);
        console.log(`  ${user.name} (${user.email})`);
        break;
        
      case 'removed':
        console.log(`User removed from index ${change.oldIndex}:`);
        console.log(`  ${user.name} (${user.email})`);
        break;
    }
  });
}

Real-time Snapshot Monitoring

import type { SnapshotListenOptions } from "@firebase/firestore-types";

function monitorCollection(collection: CollectionReference<User>) {
  // Include metadata changes in listener
  const options: SnapshotListenOptions = { 
    includeMetadataChanges: true 
  };
  
  const unsubscribe = collection.onSnapshot(options, {
    next: (snapshot: QuerySnapshot<User>) => {
      if (snapshot.metadata.hasPendingWrites) {
        console.log('Local changes pending...');
      }
      
      if (snapshot.metadata.fromCache) {
        console.log('Data loaded from cache');
      } else {
        console.log('Data loaded from server');
      }
      
      // Process document changes
      handleQueryChanges(snapshot);
    },
    error: (error) => {
      console.error('Snapshot listener error:', error);
    }
  });
  
  return unsubscribe;
}

Snapshot Comparison

function compareSnapshots(
  oldSnapshot: DocumentSnapshot<User>,
  newSnapshot: DocumentSnapshot<User>
) {
  // Check if snapshots are equal
  if (oldSnapshot.isEqual(newSnapshot)) {
    console.log('Snapshots are identical');
    return;
  }
  
  // Check if existence changed
  if (oldSnapshot.exists !== newSnapshot.exists) {
    if (newSnapshot.exists) {
      console.log('Document was created');
    } else {
      console.log('Document was deleted');
    }
    return;
  }
  
  // Compare data if both exist
  if (oldSnapshot.exists && newSnapshot.exists) {
    const oldData = oldSnapshot.data();
    const newData = newSnapshot.data();
    
    console.log('Document was modified');
    console.log('Old data:', oldData);
    console.log('New data:', newData);
  }
}