npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

firestore-helper-ts

v1.8.0

Published

A simple library for working with Firebase Firestore in TypeScript/JavaScript applications

Readme

🔥 Firestore Helper TS

Simplify your work with Firestore and save dozens of coding hours! Firestore Helper TS solves common problems when working with Firestore:

  • 🚀 Less code, more productivity - reduces boilerplate code by 60-70%
  • 🛡️ Type safety - complete TypeScript support with generic types
  • 🔄 Real-time updates - simple API for working with onSnapshot listeners
  • 🧩 Consistent data format - unified approach to processing documents and collections
  • 🚦 Structured error handling - predictable and type-safe errors
  • 📦 Minimalist size - only essential functions without unnecessary dependencies

Unlike direct use of the Firebase SDK, Firestore Helper significantly simplifies CRUD operations, provides a unified interface for working with data, and eliminates common sources of errors. Develop faster and more safely!

A simple and type-safe library for working with Firebase Firestore in TypeScript/JavaScript applications.

"Buy Me A Coffee" npm version CI

📑 Table of Contents

🚀 Installation

# NPM
npm install firestore-helper-ts

# Yarn
yarn add firestore-helper-ts

# PNPM
pnpm add firestore-helper-ts

🔑 Key Features

  • 🔥 Simple initialization of Firebase/Firestore
  • 📦 CRUD operations with TypeScript support
  • 🎯 Type-safe queries and operations
  • 🔄 Real-time updates with snapshot support
  • 🛡️ Robust error handling with custom error types
  • 🐞 Advanced logging with various levels and configuration
  • 💾 Intelligent caching for performance optimization
  • 📝 Automatic data formatting

📚 Quick Start

1. Initialize Firebase

import { initialize } from "firestore-helper-ts";

// At the beginning of your application
initialize({
  apiKey: "YOUR_API_KEY",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project",
  // other configuration...
});

2. Create a Document

import { create } from "firestore-helper-ts";

// Creates a document with automatically generated ID
const result = await create({
  path: "users", // path to collection
  data: {
    name: "John Smith",
    email: "[email protected]",
    age: 32,
    isActive: true,
  },
});

// Creates a document with custom ID (e.g., UUID)
const resultWithCustomId = await create({
  path: "users",
  customId: "123e4567-e89b-12d3-a456-426614174000", // Your custom ID
  data: {
    name: "Jane Smith",
    email: "[email protected]",
    age: 28,
    isActive: true,
  },
});

// You get back data including the ID in both cases
const userId = result.data?.id; // Firestore generated ID
const customUserId = resultWithCustomId.data?.id; // Your custom ID

3. Retrieve Data

import { get } from "firestore-helper-ts";

// Get a single document
const userResult = await get({
  path: "users",
  docId: "abc123", // document ID
});

if (userResult.data) {
  console.log(`Name: ${userResult.data.name}`);
}

// Get an entire collection with filters and sorting
const activeUsersResult = await get({
  path: "users",
  where: [
    ["isActive", "==", true], // First filter
    ["age", ">", 25], // Second filter
  ],
  orderBy: [
    ["createdAt", "desc"], // Sort by creation date descending
  ],
  limit: 10, // Limit number of results
});

// Process results
activeUsersResult.data?.forEach((user) => {
  console.log(`${user.name} (${user.email})`);
});

4. Update a Document

import { update } from "firestore-helper-ts";

// Update document
await update({
  path: "users",
  docId: "abc123",
  data: {
    age: 33,
    lastLogin: new Date(),
  },
  merge: true, // Merge with existing data (default)
  refetch: true, // Return updated data
});

5. Delete a Document

import { removeDoc } from "firestore-helper-ts";

// Delete document
await removeDoc({
  path: "users",
  docId: "abc123",
});

6. Real-time Listeners

import { listen } from "firestore-helper-ts";

// Listen to a single document
const unsubscribe = listen({
  path: "users",
  docId: "abc123",
  onNext: (userData) => {
    // Runs whenever the document changes
    console.log("User data updated:", userData);
    updateUI(userData);
  },
  onError: (error) => {
    console.error("Error listening to user:", error);
  },
});

// Listen to a collection with filters
const unsubscribeCollection = listen({
  path: "users",
  where: [["isActive", "==", true]],
  orderBy: [["lastActive", "desc"]],
  limit: 10,
  onNext: (users) => {
    console.log("Active users updated:", users);
    updateUsersList(users);
  },
});

// Stop listening when no longer needed
// For example when component unmounts
unsubscribe();
unsubscribeCollection();

7. CRUD with Real-time Updates

You can perform CRUD operations and immediately get real-time updates instead of a one-time fetch:

import { create, update, removeDoc } from "firestore-helper-ts";

// Create a document and listen for changes
const unsubscribeCreate = await create({
  path: "posts",
  data: {
    title: "New Post",
    content: "Post content...",
    createdAt: new Date(),
  },
  useListener: true, // Enable real-time listening
  onNext: (post) => {
    console.log("Post created or updated:", post);
    updateUI(post);
  },
});

// Update a document and listen for changes
const unsubscribeUpdate = await update({
  path: "posts",
  docId: "post123",
  data: { likes: 42 },
  useListener: true,
  onNext: (post) => {
    console.log("Post updated:", post);
    updateUI(post);
  },
});

// Later, stop listening
unsubscribeCreate();
unsubscribeUpdate();

🔄 Import Methods

The library offers flexibility when importing functions:

Standard Function Names

import {
  getData,
  updateData,
  createData,
  deleteData,
  listenData,
} from "firestore-helper-ts";

// Usage
const result = await getData({ path: "users", docId: "abc123" });

Short Aliases (recommended)

import { get, update, create, removeDoc, listen } from "firestore-helper-ts";

// Usage
const result = await get({ path: "users", docId: "abc123" });

As an Object

import FirestoreHelper from "firestore-helper-ts";

// Usage
const result = await FirestoreHelper.get({ path: "users", docId: "abc123" });

🌟 Examples

Check out the examples directory for usage examples with different frameworks:

Basic Examples

Framework Integration

For more details and instructions on running these examples, check the examples README.

🦺 TypeScript Support

Firestore Helper TS provides complete TypeScript support with generic types for all operations:

// Define your data types
interface User {
  id?: string;
  name: string;
  email: string;
  age: number;
  isActive: boolean;
  createdAt: Date;
}

interface Post {
  id?: string;
  title: string;
  content: string;
  authorId: string;
  tags: string[];
  publishedAt?: Date;
}

// Use with CRUD operations
const userResult = await get<User>({
  path: "users",
  docId: "user123",
});

const postsResult = await get<Post[]>({
  path: "posts",
  where: [["authorId", "==", "user123"]],
});

// Type-safe real-time listeners
listen<User>({
  path: "users",
  docId: "user123",
  onNext: (user) => {
    if (user) {
      // TypeScript knows all available properties
      console.log(`${user.name} (${user.email})`);
    }
  },
});

🛡️ Error Handling

Firestore Helper TS includes a comprehensive error handling system that provides:

  • Type-safe error handling with custom error types
  • Detailed error messages and codes
  • Consistent error format across all operations
  • Built-in error types for common scenarios
import {
  FirestoreHelperError,
  ValidationError,
  PermissionError,
  NotFoundError,
} from "firestore-helper-ts";

try {
  const result = await get({
    path: "users",
    docId: "user123",
  });

  if (result.error) {
    // Type-safe error handling
    switch (result.error.constructor) {
      case ValidationError:
        console.error("Invalid parameters:", result.error.message);
        break;
      case PermissionError:
        console.error("Permission denied:", result.error.message);
        // Redirect to login
        break;
      case NotFoundError:
        console.error("Document not found:", result.error.message);
        // Show empty state
        break;
      default:
        console.error("Unexpected error:", result.error.message);
    }
  } else {
    // Process result.data
    console.log("User data:", result.data);
  }
} catch (error) {
  // Handle unexpected errors
  console.error("Critical error:", error);
}

Error Types

  • ValidationError: Invalid parameters or data format
  • PermissionError: Insufficient permissions
  • NotFoundError: Document or collection not found
  • InitializationError: Firebase not properly initialized
  • NetworkError: Network-related issues
  • TimeoutError: Operation timeout
  • CacheError: Caching-related issues

💾 Caching System

Firestore Helper TS includes an intelligent caching system to optimize performance and reduce Firestore reads:

import { configureCache, get } from "firestore-helper-ts";

// Configure global cache settings
configureCache({
  enabled: true, // Enable/disable caching
  maxSize: 1000, // Maximum number of cached items
  ttl: 5 * 60 * 1000, // Time-to-live in milliseconds (5 minutes)
});

// Use cache with get operations
const result = await get({
  path: "users",
  docId: "user123",
  cache: {
    enabled: true, // Enable for this request
    ttl: 60000, // Custom TTL for this request (1 minute)
  },
});

// Cache with collection queries
const activeUsers = await get({
  path: "users",
  where: [["isActive", "==", true]],
  cache: {
    enabled: true,
    key: "active-users", // Custom cache key
  },
});

// Clear cache for specific paths
await clearCache("users/*");

// Clear all cache
await clearCache();

// Get cache statistics
const stats = getCacheStats();
console.log("Cache hits:", stats.hits);
console.log("Cache misses:", stats.misses);
console.log("Items in cache:", stats.size);

Cache Features

  • Configurable TTL (Time-to-Live)
  • Maximum cache size limit
  • Custom cache keys
  • Cache invalidation patterns
  • Cache statistics and monitoring
  • Per-request cache configuration
  • Automatic cache cleanup

🐞 Debugging & Logging

Firestore Helper TS includes a comprehensive logging system that helps you debug your application and understand what's happening under the hood.

Logging Levels

The logging system supports various levels of detail:

import { LogLevel, configureLogger } from "firestore-helper-ts";

// Available log levels
LogLevel.NONE; // 0: No logging
LogLevel.ERROR; // 1: Only errors (default)
LogLevel.WARN; // 2: Errors and warnings
LogLevel.INFO; // 3: Errors, warnings, and informational messages
LogLevel.DEBUG; // 4: All messages including detailed debug info

Basic Usage

To change the logging level:

import { configureLogger, LogLevel } from "firestore-helper-ts";

// Enable all logs including debug
configureLogger({ level: LogLevel.DEBUG });

// Only show errors and warnings
configureLogger({ level: LogLevel.WARN });

// Disable all logging
configureLogger({ level: LogLevel.NONE });

Creating Custom Loggers

You can create dedicated loggers for different parts of your application:

import { createLogger } from "firestore-helper-ts";

// Create loggers for different components
const authLogger = createLogger("auth");
const dbLogger = createLogger("database");
const uiLogger = createLogger("ui");

// Use them in your code
authLogger.info("User logged in successfully");
dbLogger.debug("Fetching data with params", { collection: "users", id: "123" });
uiLogger.warn("Component will be deprecated in next version");

Customizing Log Format

You can customize how logs are formatted:

// Configure timestamp and operation name display
configureLogger({
  timestamps: true, // Include timestamps in logs
  showOperation: true, // Include operation name in logs
});

Custom Log Handling

For production environments, you might want to send logs to a monitoring service:

// Set up a custom handler for all logs
configureLogger({
  level: LogLevel.ERROR, // Only process errors
  customHandler: (level, message, ...data) => {
    // Send to your logging service
    myLoggingService.capture({
      level: LogLevel[level],
      message,
      data,
      timestamp: new Date().toISOString(),
    });
  },
});

Logging in Practice

In development, enable detailed logging to see what's happening:

// During development
configureLogger({ level: LogLevel.DEBUG });

// Later in production
configureLogger({ level: LogLevel.ERROR });

For a complete example of using the logging system, check examples/logging-example.ts.

📋 Complete CRUD Application

import {
  initialize,
  get,
  create,
  update,
  removeDoc,
  listen,
  Result,
} from "firestore-helper-ts";

// Type definitions
interface Product {
  id?: string;
  name: string;
  price: number;
  stock: number;
  categories: string[];
}

// Initialize Firebase
initialize({
  /* configuration */
});

// Create product
async function createProduct(
  productData: Omit<Product, "id">
): Promise<string | null> {
  const result = await create<Product>({
    path: "products",
    data: productData,
  });

  return result.data?.id || null;
}

// Get product by ID
async function getProduct(productId: string): Promise<Product | null> {
  const result = await get<Product>({
    path: "products",
    docId: productId,
  });

  return result.data;
}

// Get products by category
async function getProductsByCategory(category: string): Promise<Product[]> {
  const result = await get<Product[]>({
    path: "products",
    where: [["categories", "array-contains", category]],
    orderBy: [["price", "asc"]],
  });

  return result.data || [];
}

// Update stock
async function updateStock(
  productId: string,
  newStock: number
): Promise<boolean> {
  const result = await update({
    path: "products",
    docId: productId,
    data: { stock: newStock },
  });

  return !result.error;
}

// Delete product
async function deleteProduct(productId: string): Promise<boolean> {
  const result = await removeDoc({
    path: "products",
    docId: productId,
  });

  return !result.error;
}

// Listen to stock changes
function listenToStockChanges(
  productId: string,
  callback: (stock: number) => void
): () => void {
  return listen<Product>({
    path: "products",
    docId: productId,
    onNext: (product) => {
      if (product) {
        callback(product.stock);
      }
    },
  });
}

// Using the functions
async function manageInventory() {
  // Create product
  const newProductId = await createProduct({
    name: "Smartphone",
    price: 699.99,
    stock: 10,
    categories: ["electronics", "phones"],
  });

  // Get product
  const product = await getProduct(newProductId!);
  console.log(`Product: ${product?.name}, Price: $${product?.price}`);

  // Update stock
  await updateStock(newProductId!, 8);

  // Get products by category
  const phones = await getProductsByCategory("phones");
  console.log(`Found ${phones.length} phones`);

  // Set up real-time listener for stock changes
  const unsubscribe = listenToStockChanges(newProductId!, (newStock) => {
    console.log(`Stock changed: ${newStock} units available`);
    updateStockDisplay(newStock);
  });

  // Later, when no longer needed
  unsubscribe();

  // Delete product
  await deleteProduct(newProductId!);
}

⚡ Advanced Usage

Complex Queries with Multiple Conditions

import { get } from "firestore-helper-ts";

// Find active premium users who have logged in recently
const result = await get({
  path: "users",
  where: [
    ["isActive", "==", true],
    ["subscriptionTier", "==", "premium"],
    ["lastLogin", ">", new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)], // 7 days ago
  ],
  orderBy: [
    ["lastLogin", "desc"], // Most recent logins first
  ],
  limit: 20,
});

// Process the results
result.data?.forEach((user) => {
  console.log(
    `Premium user ${user.name} last logged in on ${user.lastLogin.toDate()}`
  );
});

Working with Subcollections

import { create, get, update, listen } from "firestore-helper-ts";

// Create a parent document
const { data: organization } = await create({
  path: "organizations",
  data: { name: "Acme Inc.", founded: 1985 },
});

// Add a document to a subcollection
const orgId = organization.id;
await create({
  path: `organizations/${orgId}/departments`,
  data: { name: "Engineering", headCount: 25 },
});

// Get all departments for an organization
const { data: departments } = await get({
  path: `organizations/${orgId}/departments`,
});

// Listen to changes in departments
const unsubscribe = listen({
  path: `organizations/${orgId}/departments`,
  onNext: (departments) => {
    console.log("Departments updated:", departments);
    updateDepartmentsList(departments);
  },
});

Real-time Dashboard Example

import { listen } from "firestore-helper-ts";

function setupDashboard() {
  // Listen to active orders
  const unsubscribeOrders = listen({
    path: "orders",
    where: [["status", "==", "active"]],
    orderBy: [["createdAt", "desc"]],
    onNext: (orders) => {
      updateOrdersDisplay(orders);

      // Calculate total revenue
      const totalRevenue = orders.reduce((sum, order) => sum + order.total, 0);
      updateRevenueDisplay(totalRevenue);
    },
  });

  // Listen to inventory levels
  const unsubscribeInventory = listen({
    path: "products",
    where: [["stock", "<", 10]], // Low stock items
    onNext: (lowStockProducts) => {
      updateLowStockAlerts(lowStockProducts);
    },
  });

  // Listen to new user registrations
  const unsubscribeUsers = listen({
    path: "users",
    orderBy: [["createdAt", "desc"]],
    limit: 5,
    onNext: (recentUsers) => {
      updateRecentUsersWidget(recentUsers);
    },
  });

  // Return a function to unsubscribe from all listeners
  return () => {
    unsubscribeOrders();
    unsubscribeInventory();
    unsubscribeUsers();
  };
}

// In a React component:
// useEffect(() => {
//   const unsubscribeAll = setupDashboard();
//   return () => unsubscribeAll();
// }, []);

Custom Document IDs

You can use your own document IDs (like UUID) when creating documents:

import { v4 as uuidv4 } from "uuid";
import { create } from "firestore-helper-ts";

// Create a document with UUID
const result = await create({
  path: "users",
  customId: uuidv4(), // Use UUID as document ID
  data: {
    name: "John Doe",
    email: "[email protected]",
  },
});

// The document will be created with your custom ID
console.log("Created user with custom ID:", result.data?.id);

This is useful when:

  • You need to know the document ID before creation
  • You want to use UUIDs or other custom ID formats
  • You need to ensure ID uniqueness across different Firestore instances
  • You're migrating data from another system and want to preserve IDs

🌟 Examples

Basic Examples

Check out our examples directory for complete working examples:

  • basic.ts - Basic CRUD operations
  • typescript-example.ts - TypeScript integration
  • error-handling-example.ts - Error handling patterns
  • caching-example.ts - Caching implementation
  • real-time-example.ts - Real-time listeners

Framework Integration

We provide examples for popular frameworks:

  • React: react-example.tsx
  • Next.js: nextjs-example.tsx
  • Vue.js: vue-example.ts

🧪 Testing

The library contains unit tests to ensure proper functionality. Tests are written using the Jest framework.

Running Tests

# Run all tests
npm test

# Run tests with watch mode
npm run test:watch

# Run tests with code coverage
npm run test:coverage

🔄 Continuous Integration and Deployment

This repository uses GitHub Actions to automate the development, testing, and release process.

CI Workflow

  • Automatic test execution on every push or pull request to main branches
  • Testing on multiple Node.js versions (16.x, 18.x, 20.x)
  • Automatic builds to verify compatibility

📄 License

ISC