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

sequelize-memory-clone

v0.0.1-alpha.5

Published

Enhanced Sequelize caching for Redis/ioredis. Provides deep entity hydration and automatic mass query invalidation on writes.

Readme

sequelize-memory-clone

npm version TypeScript

An enhanced caching layer for Sequelize, transforming your Redis instance into a near real-time, in-memory clone of your database for frequently accessed data.

This package provides deep, associative caching for individual entities and mass invalidation for query results, ensuring consistency for high-scale read operations.

✨ Features

  • Entity Caching (Write-Through): Caches individual model instances (findByPk, create) upon retrieval or modification.

  • Query Cache with Mass Invalidation: Caches results from findAll, findOne, and findAndCountAll. Crucially, any write operation (save, destroy, update, upsert, create) on the model automatically invalidates ALL stored query caches for that model.

  • Deep Hydration: Cached results are re-hydrated into fully functional Sequelize instances, preserving all associated models (including nested includes) as living Sequelize objects.

  • TypeScript Support: Built entirely in TypeScript for strong typing and dual CJS/ESM compatibility.

  • ioredis Adapter Included: Ships ready to use with the high-performance ioredis client.

🚀 Installation

This library requires sequelize, ioredis, and your choice of database driver.

# Install the core library
npm install sequelize-memory-clone sequelize ioredis

🛠️ Usage

1. Setup (Adapter & Wrapper)

First, initialize your Redis client and the sequelize-memory-clone wrapper.

import { Sequelize, DataTypes } from 'sequelize';
import Redis from 'ioredis';
import { withCache, IORedisAdapter } from 'sequelize-memory-clone';

// 1. Initialize Redis Client
const redisClient = new Redis();
const redisAdapter = new IORedisAdapter({
  client: redisClient,
  namespace: 'app-cache',
  lifetime: 3600 // Cache TTL: 1 hour
});

// 2. Initialize the Cache Wrapper
const { withCache: cacheWrapper } = withCache(redisAdapter);

// 3. Setup Sequelize
const sequelize = new Sequelize('sqlite::memory:');

const User = sequelize.define('User', {
    id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
    name: DataTypes.STRING,
});

const Post = sequelize.define('Post', {
    title: DataTypes.STRING,
    userId: DataTypes.INTEGER
});
User.hasMany(Post, { foreignKey: 'userId', as: 'Posts' });

// 4. Wrap the Models to enable caching features
const CacheUser = cacheWrapper(User);
const CachePost = cacheWrapper(Post);

// Now use CacheUser and CachePost everywhere!

2. Cache Methods in Action

Access all caching features explicitly through the .cache() call.

| Operation | Method | Behavior | Goal | |---|---|---|---| | Get (Entity) | CacheUser.cache().findByPk(1) | Checks cache first. If Miss, fetches from DB, caches result (Write-Through). | High-speed single entity lookups. | | Get (Query) | CacheUser.cache('active_q').findAll(...) | Checks cache first using the provided key. If Miss, fetches, caches result set, and tracks the query key. | Accelerate complex queries. | | Write | userInstance.save() | Writes to DB. Updates the individual entity cache (User:1). Invalidates ALL tracked query caches for User. | Ensures query results are fresh after writes. | | Refresh | userInstance.cache().reload() | Bypasses cache, fetches fresh data from DB, and updates the entity cache with the new data. | Corrects stale data after external database writes. |

💻 Example Test Flow (What the Code Does)

The following sequence demonstrates the core functionality:

// --- TEST FLOW ---

// 1. Create: Writes to DB and populates entity cache. (Cache: [User:1] = { name: Alice })
const user = await CacheUser.create({ id: 1, name: 'Alice', status: 'active' }); 
await CachePost.create({ userId: 1, title: 'Post A' });

// 2. Query Miss/Set: Fills the query cache. (Cache: [query:active_q] = [User:1 with Posts])
await CacheUser.cache('active_list').findAll({ where: { status: 'active' }, include: 'Posts' });
// DB Query Executed.

// 3. External Write: Bypasses cache and updates DB only.
await User.update({ name: 'Alicia' }, { where: { id: user.id } });
// DB name is now 'Alicia', Cache name is still 'Alice'.

// 4. Stale Read: Gets old data from cache.
let staleUser = await CacheUser.cache().findByPk(user.id);
console.log(staleUser.name); // Output: Alice 

// 5. Force Reload: Bypasses cache, gets fresh data, and updates the entity cache.
await staleUser.cache().reload(); 
console.log(staleUser.name); // Output: Alicia 

// 6. Write Operation: Static Update
await CacheUser.cache().update({ name: 'ALICIA' }, { where: { id: user.id } });
// Cache Hook triggers: DEL [User:1], DEL [query:active_q], SET [User:1] (new data)

// 7. Query Re-check: Confirms invalidation.
await CacheUser.cache('active_list').findAll({ where: { status: 'active' }, include: 'Posts' });
// DB Query Executed (Cache Miss) - The mass invalidation worked!

🤝 Contributing

Contributions, issues, and feature requests are welcome! Feel free to check the issues page if you have any questions or would like to submit a Pull Request.

Development Setup

This package uses TypeScript and relies on ioredis as the default adapter.

1. Clone the Repository:

git clone [email protected]:GoldenDev4825/sequelize-memory-clone.git
cd sequelize-memory-clone
npm install

2. Build Code:

npm run build

This compiles the TypeScript (src/) into CJS and ESM formats in the dist/ directory.

3. Local Testing: Use npm link to test your changes in a local application environment:

# In the library directory
npm link

# In your separate test project directory
npm link sequelize-memory-clone

Adapter Development

The core interface is simple, allowing for easy adaptation to other cache systems:

export interface CacheAdapter {
  get(key: string[]): Promise<any | null>;
  set(key: string[], value: any, options?: { lifetime?: number }): Promise<void>;
  del(key: string[]): Promise<void>;
  // Crucial methods for mass invalidation:
  delModelQueries(modelName: string): Promise<void>;
  addQueryKey(modelName: string, queryKey: string[]): Promise<void>;
}