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

flamecache

v1.0.2

Published

Simple and robust caching layer using Firebase Realtime Database

Downloads

61

Readme

Flamecache

npm version License: MIT TypeScript Bundle size

A Redis-like caching layer for Firebase Realtime Database with automatic expiration, counters, and batch operations.

Flamecache is a lightweight, robust caching solution that brings Redis-style operations to Firebase Realtime Database. Perfect for serverless applications, real-time apps, and projects already using Firebase.

✨ Features

  • 🚀 Redis-like API - Familiar get, set, incr, decr, expire operations
  • Fast & Lightweight - Only ~1KB (minified), no heavy dependencies
  • 🔄 Auto-expiration - Built-in TTL support with automatic cleanup
  • 📊 Counters - Atomic increment/decrement operations
  • 📦 Batch Operations - Multi-get, multi-set, multi-delete
  • 🎯 TypeScript - Full type safety with generics
  • 🔌 Zero Config - Works out of the box
  • 🌐 Serverless Ready - Perfect for Firebase Functions, Vercel, Netlify

📦 Installation

npm install flamecache firebase

Flamecache requires Firebase as a peer dependency. If you don't have Firebase installed:

npm install firebase flamecache

🚀 Quick Start

import { createCache } from 'flamecache';

// Initialize cache
const cache = createCache({
  firebase: {
    apiKey: 'your-api-key',
    databaseURL: 'https://your-project.firebaseio.com',
    projectId: 'your-project-id',
  },
  rootPath: 'my-cache', // Root path in Firebase (default: 'flamecache')
  ttl: 3600, // Default TTL: 1 hour
  disableCache: false, // Optional: disable cache interactions (useful for testing/development)
});

// Basic usage
await cache.set('user:123', { name: 'Alice', email: '[email protected]' });
const user = await cache.get('user:123');

// With custom TTL (in seconds)
await cache.set('temp:token', 'abc123', 300); // Expires in 5 minutes

// Counters
await cache.incr('views:post:456'); // Increment view count
const views = await cache.get('views:post:456'); // 1

// Read-through caching
const posts = await cache.wrap(
  'posts:latest',
  async () => {
    // This function only runs on cache miss
    const response = await fetch('https://api.example.com/posts');
    return response.json();
  },
  600
); // Cache for 10 minutes

📖 API Reference

Core Operations

get<T>(key: string): Promise<T | null>

Retrieve a value from cache.

const user = await cache.get<User>('user:123');
if (user) {
  console.log(user.name);
}

set<T>(key: string, data: T, ttl?: number): Promise<void>

Store a value in cache with optional TTL (in seconds).

// With default TTL
await cache.set('user:123', userData);

// With custom TTL (10 minutes)
await cache.set('session:abc', sessionData, 600);

// Never expires
await cache.set('config', configData, 0);

del(key: string): Promise<void>

Delete a key from cache.

await cache.del('user:123');

has(key: string): Promise<boolean>

Check if a key exists and is not expired.

if (await cache.has('session:abc')) {
  console.log('Session is valid');
}

clear(): Promise<void>

Clear all cache entries.

await cache.clear();

disconnect(): Promise<void>

Close the connection to Firebase. Crucial for Node.js scripts to exit cleanly.

await cache.disconnect();

Counter Operations

incr(key: string, by?: number): Promise<number>

Increment a numeric value. Returns the new value.

const views = await cache.incr('views:post:123'); // Increment by 1
const score = await cache.incr('score:player', 10); // Increment by 10

decr(key: string, by?: number): Promise<number>

Decrement a numeric value. Returns the new value.

const stock = await cache.decr('stock:item:456'); // Decrement by 1
const credits = await cache.decr('credits:user', 5); // Decrement by 5

TTL Operations

expire(key: string, ttl: number): Promise<boolean>

Set or update expiration time for an existing key (in seconds).

await cache.expire('session:abc', 300); // Expire in 5 minutes
await cache.expire('permanent:key', 0); // Remove expiration

getTtl(key: string): Promise<number>

Get remaining time-to-live in seconds.

  • Returns 0 if key doesn't exist or is expired
  • Returns -1 if key has no expiration
  • Returns remaining seconds otherwise
const remaining = await cache.getTtl('session:abc');
if (remaining > 0 && remaining < 300) {
  console.log('Session expiring soon!');
}

touch(key: string, ttl?: number): Promise<boolean>

Refresh TTL without changing the value.

// Use default TTL
await cache.touch('session:abc');

// Use custom TTL
await cache.touch('session:abc', 3600);

Batch Operations

mget<T>(keys: string[]): Promise<(T | null)[]>

Get multiple keys at once.

const [user1, user2, user3] = await cache.mget(['user:1', 'user:2', 'user:3']);

mset(entries: Record<string, any>, ttl?: number): Promise<void>

Set multiple keys at once.

await cache.mset(
  {
    'user:1': userData1,
    'user:2': userData2,
    'user:3': userData3,
  },
  600
); // All expire in 10 minutes

mdel(keys: string[]): Promise<void>

Delete multiple keys at once.

await cache.mdel(['temp:1', 'temp:2', 'temp:3']);

Advanced Operations

wrap<T>(key: string, fetchFn: () => Promise<T>, ttl?: number): Promise<T>

Read-through caching pattern. Gets from cache or fetches and stores automatically.

const userData = await cache.wrap(
  'user:123',
  async () => {
    // This only runs on cache miss
    const response = await fetch(`/api/users/123`);
    return response.json();
  },
  300
);

pull<T>(key: string): Promise<T | null>

Get and delete in one operation (atomic pop).

const token = await cache.pull('temp:verification-token');
// Token is retrieved and deleted

🎯 Use Cases

1. Rate Limiting

async function checkRateLimit(userId: string): Promise<boolean> {
  const key = `ratelimit:${userId}`;
  const count = await cache.incr(key);

  // Set expiration on first request
  if (count === 1) {
    await cache.expire(key, 60); // Reset every minute
  }

  return count <= 100; // Max 100 requests per minute
}

// Usage
if (!(await checkRateLimit('user123'))) {
  throw new Error('Rate limit exceeded');
}

2. Session Management

class SessionManager {
  async create(userId: string, data: any): Promise<string> {
    const sessionId = generateId();
    await cache.set(
      `session:${sessionId}`,
      {
        userId,
        ...data,
        createdAt: Date.now(),
      },
      3600
    ); // 1 hour session
    return sessionId;
  }

  async get(sessionId: string) {
    return await cache.get(`session:${sessionId}`);
  }

  async extend(sessionId: string): Promise<boolean> {
    return await cache.touch(`session:${sessionId}`, 3600);
  }

  async destroy(sessionId: string): Promise<void> {
    await cache.del(`session:${sessionId}`);
  }
}

3. API Response Caching

async function getUser(userId: string) {
  return cache.wrap(
    `api:user:${userId}`,
    async () => {
      console.log('Fetching from API...');
      const response = await fetch(`https://api.example.com/users/${userId}`);
      return response.json();
    },
    600
  ); // Cache for 10 minutes
}

// First call - fetches from API
const user1 = await getUser('123');

// Second call - uses cache (within 10 minutes)
const user2 = await getUser('123');

4. Leaderboard / Scoring

async function addScore(playerId: string, points: number) {
  await cache.incr(`score:${playerId}`, points);
}

async function getTopScores(playerIds: string[]) {
  const keys = playerIds.map((id) => `score:${id}`);
  const scores = await cache.mget<number>(keys);

  return playerIds
    .map((id, i) => ({
      playerId: id,
      score: scores[i] || 0,
    }))
    .sort((a, b) => b.score - a.score);
}

5. Temporary Tokens

async function createVerificationToken(email: string): Promise<string> {
  const token = generateToken();
  await cache.set(`verify:${token}`, { email }, 900); // 15 minutes
  return token;
}

async function verifyToken(token: string) {
  // Pull removes the token after reading (one-time use)
  const data = await cache.pull(`verify:${token}`);
  if (!data) {
    throw new Error('Invalid or expired token');
  }
  return data;
}

6. Real-time Analytics

// Track page views
async function trackPageView(pageId: string) {
  await cache.incr(`views:${pageId}`);
  await cache.incr(`views:today:${pageId}`);

  // Expire daily stats at midnight
  const secondsUntilMidnight = getSecondsUntilMidnight();
  await cache.expire(`views:today:${pageId}`, secondsUntilMidnight);
}

// Get analytics
async function getAnalytics(pageIds: string[]) {
  const totalKeys = pageIds.map((id) => `views:${id}`);
  const todayKeys = pageIds.map((id) => `views:today:${id}`);

  const [totalViews, todayViews] = await Promise.all([
    cache.mget<number>(totalKeys),
    cache.mget<number>(todayKeys),
  ]);

  return pageIds.map((id, i) => ({
    pageId: id,
    total: totalViews[i] || 0,
    today: todayViews[i] || 0,
  }));
}

📊 Performance

Here are the benchmark results running against a real Firebase Realtime Database instance (50 iterations per operation):

| Operation | Avg Latency | Throughput | | :--------------------- | :--------------- | :---------------- | | GET (Cache Hit) | ~110ms | 9 ops/sec | | SET (Write) | ~120ms | 8 ops/sec | | INCR (Atomic) | ~230ms | 4 ops/sec | | MSET (Batch Write) | ~2.4ms / key | 415+ keys/sec | | MGET (Batch Read) | ~3.6ms / key | 275+ keys/sec |

All benchmarks include real-world network latency to Firebase. Batch operations leverage parallelization and atomic multi-path updates for maximum efficiency.

You can run these benchmarks yourself:

npm run example benchmark

See the benchmark script for details.

🔧 Configuration

interface CacheConfig {
  // Firebase configuration (required)
  firebase: {
    apiKey: string;
    authDomain?: string;
    databaseURL: string;
    projectId: string;
    storageBucket?: string;
    messagingSenderId?: string;
    appId?: string;
  };

  // Root path in Firebase database (default: 'flamecache')
  rootPath?: string;

  // Default TTL in seconds (default: 3600 = 1 hour, 0 = never expires)
  ttl?: number;

  // Enable debug logs (default: false)
  debug?: boolean;

  // Disable all cache operations (default: false)
  // When true: get/mget return null, set/mset/del do nothing, wrap always fetches
  disableCache?: boolean;
}

// Example with all options
const cache = createCache({
  firebase: {
    /* ... */
  },
  rootPath: 'my-cache',
  ttl: 7200, // 2 hours
  debug: true, // Log all operations
});

🔑 Key Naming Conventions

Use colons (:) to create hierarchical keys for better organization:

// Good ✅
'user:123';
'user:123:profile';
'session:abc123';
'api:github:users:456';
'ratelimit:user:789';
'cache:posts:latest';

// Avoid ❌
'user_123';
'user-profile-123';
'session.abc123';

🎨 TypeScript Support

Full TypeScript support with generics:

interface User {
  id: string;
  name: string;
  email: string;
}

// Type-safe operations
const user = await cache.get<User>('user:123');
// user is typed as User | null

await cache.set<User>('user:123', {
  id: '123',
  name: 'Alice',
  email: '[email protected]',
});

// Works with complex types
type ApiResponse = {
  data: User[];
  meta: { total: number };
};

const response = await cache.wrap<ApiResponse>('api:users', fetchUsers);

🔒 Firebase Security Rules

Development

{
  "rules": {
    "flamecache": {
      ".read": true,
      ".write": true
    }
  }
}

Production

{
  "rules": {
    "flamecache": {
      ".read": "auth != null",
      ".write": "auth != null"
    }
  }
}

Advanced (per-user caching)

{
  "rules": {
    "flamecache": {
      "users": {
        "$uid": {
          ".read": "$uid === auth.uid",
          ".write": "$uid === auth.uid"
        }
      },
      "public": {
        ".read": true,
        ".write": "auth != null"
      }
    }
  }
}

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

# Clone the repo
git clone https://github.com/iamanishroy/flamecache.git
cd flamecache

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

📝 License

MIT © Anish Roy

🔗 Links