@nasriya/cachify
Version:
A lightweight, extensible in-memory caching library for storing anything, with built-in TTL and customizable cache types.
332 lines (249 loc) β’ 13.2 kB
Markdown
<img src="./.github/assets/Cachify_Full_Logo.svg" height="80px" alt="Cachify Logo" center>
# Cachify
Cachify is a fast, flexible caching library for Node.js supporting key-value and file caching with multi-storage and lifecycle management.
[](https://github.com/nasriyasoftware/Cachify?tab=License-1-ov-file)    [](link-to-your-status-page)
##### Visit us at [www.nasriya.net](https://nasriya.net).
Made with β€οΈ in **Palestine** π΅πΈ
___
## Overview
Cachify is a fast, flexible caching library for Node.js that supports both key-value and file caching across multiple storage backends, with built-in lifecycle management and persistence.
This documentation covers the essential concepts, setup, and best practices to get started quickly and efficiently.
### Contents
- [Why Cachify and When to Use It](#why-cachify-and-when-to-use-it)
- [Key Advantages](#key-advantages)
- [When to Use Cachify Instead of Just Redis](#when-to-use-cachify-instead-of-just-redis)
- [Installation \& Importing](#installation--importing)
- [Installation](#installation)
- [Importing](#importing)
- [Usage](#usage)
- [1. Creating Clients (Isolation)](#1-creating-clients-isolation)
- [2. Registering Storage Engines](#2-registering-storage-engines)
- [3. Default Storage Engines](#3-default-storage-engines)
- [4. Caching (KVS \& Files)](#4-caching-kvs--files)
- [5. Persistence \& Cold Start Recovery](#5-persistence--cold-start-recovery)
- [Testing](#testing)
- [Required Environment Variables](#required-environment-variables)
- [Running Tests](#running-tests)
- [Benchmarking](#benchmarking)
- [Environment Setup](#environment-setup)
- [Optional Environment Variables](#optional-environment-variables)
- [Running Benchmarks](#running-benchmarks)
- [License](#license)
> [!IMPORTANT]
>
> π **Support Our Open-Source Development!** π
> We need your support to keep our projects going! If you find our work valuable, please consider contributing. Your support helps us continue to develop and maintain these tools.
>
> **[Click here to support us!](https://fund.nasriya.net/)**
>
> Every contribution, big or small, makes a difference. Thank you for your generosity and support!
___
## Why Cachify and When to Use It
Cachify goes **beyond traditional key-value caching** like Redis, offering a unified, flexible, and highly extensible caching solution for Node.js.
### Key Advantages
- **Unified caching for keys and files**: Cache key-value pairs and files in memory, Redis, or other backends seamlessly.
- **Multi-storage support**: Combine memory, Redis, and custom engines for redundancy, failover, and multi-region setups.
- **Automated file lifecycle management**: Automatic revalidation, eviction, and TTL handling.
- **Persistence & cold-start recovery**: Backup and restore your cache to survive application restarts without rebuilding.
- **Extensibility**: Easily plug in custom storage engines or adapters.
- **Fastest read-first strategy**: Queries multiple backends and returns the first successful result to optimize latency.
- **Isolation**: Multiple cache clients can coexist safely, even when sharing storage engines like Redis.
### When to Use Cachify Instead of Just Redis
- You need **file caching in memory or Redis**, with **automatic retrieval by file path**.
- You want **automatic file revalidation** without writing extra watchers.
- Your application requires **multi-storage redundancy** or **fastest-response reads** across multiple backends.
- You need **persistent caching** that survives cold starts without rebuilding.
- You want a **Node.js-native, highly extensible caching library** that complements or goes beyond Redis.
___
## Installation & Importing
### Installation
```bash
npm install @nasriya/cachify
```
## Importing
Importing in **ESM** modules
```js
import cachify from '@nasriya/cachify';
```
Importing in **CommonJS** modules
```js
const cachify = require('@nasriya/cachify').default;
```
___
## Usage
This section guides you through Cachifyβs core usage in a logical order.
### 1. Creating Clients (Isolation)
The imported `cachify` object is an instance of the **`Cachify`** class,
which **extends `CachifyClient`**. It behaves like a cache client but also provides global utilities such as debugging, configuration, and client creation.
You can create **isolated clients** for independent caches β useful for multi-tenant systems, different environments, or testing.
```js
import cachify from '@nasriya/cachify';
// Create an isolated instance
const isolated = cachify.createClient();
// Each instance maintains its own managers, engines, and configurations
await isolated.kvs.set('1', 'value');
await cachify.kvs.get('1'); // β undefined β isolated from the other instance
// Both share the same API surface, but only `cachify` includes utilities like:
console.log(cachify.debug); // Access global debug utilities
console.log(cachify.createClient); // Create new isolated clients
```
**Notes:**
- Isolated clients behave like `CachifyClient` instances but also include additional properties and methods from `Cachify`, such as `debug` and `createClient`.
- All usage patterns shown for the global `cachify` instance apply to isolated clients.
- Useful for multi-tenant systems, testing, or environments requiring independent caches.
### 2. Registering Storage Engines
Before storing or reading records in a backend like Redis, you must register your storage engines:
```js
import { createClient } from '@redis/client';
// Create Redis clients
const redisEU = createClient({ url: process.env.REDIS_EU });
const redisUS = createClient({ url: process.env.REDIS_US });
// Register the clients with Cachify
cachify.engines.useRedis('redis-eu', redisEU);
cachify.engines.useRedis('redis-us', redisUS);
// Register a custom engine
cachify.engines.defineEngine('custom-engine', {
onSet: async (record, value, context) => {
context.set(record.key, value);
},
onRead: async (record, context): Promise<any> => {
return context.get(record.key);
},
onRemove: async (record, context): Promise<void> => {
if (context.has(record.key)) { context.delete(record.key) }
}
});
```
**Notes:**
- Any backend (e.g., Redis) must be registered with Cachify before use.
- Names used in `storeIn` or `defaultEngines` must exactly match registered engine names.
- Attempting to use an unregistered engine will throw an error at runtime.
- Redis integration is optional; Cachify defaults to in-memory storage if no backend is configured.
### 3. Default Storage Engines
You can set default engines for any record manager (kvs, files, etc.) to avoid specifying storeIn on every operation:
```js
// For key-value records
cachify.kvs.defaultEngines = 'redis-eu';
// Or multiple engines
cachify.kvs.defaultEngines = ['redis-eu'];
// Redundant / multi-region setup
cachify.kvs.defaultEngines = ['redis-eu', 'redis-us'];
// For file records
cachify.files.defaultEngines = 'redis-eu';
cachify.files.defaultEngines = ['redis-eu', 'redis-us'];
```
**Notes:**
- Operations without a `storeIn` option automatically use the defined `defaultEngines`.
- Applies to all record managers (`kvs`, `files`, and custom managers).
- Multiple engines allow redundancy, failover, or multi-region caching.
- Engines must be registered before assigning them as defaults.
- Simplifies code and ensures consistent storage behavior across operations.
### 4. Caching (KVS & Files)
Key-Value Caching (KVS)
```js
// Recommended: set a record in the "users" scope
await cachify.kvs.set('1', { name: 'Ahmad' }, { scope: 'users' });
// Retrieve from the same scope
const user = await cachify.kvs.get('1', 'users');
console.log(user); // { name: 'Ahmad' }
// Optional: store in multiple backends
await cachify.kvs.set('2', { name: 'Omar' }, { scope: 'users', storeIn: ['memory', 'redis-eu'] });
const user2 = await cachify.kvs.get('2', 'users');
console.log(user2); // { name: 'Omar' }
// Scopes are optional β default is "global"
await cachify.kvs.set('site', { name: 'Cachify' });
const site = await cachify.kvs.get('site');
console.log(site); // { name: 'Cachify' }
```
File Caching
```js
const filePath = 'path/to/file.txt';
// Cache a file in the "documents" scope
await cachify.files.set(filePath, { scope: 'documents' });
// Inspect file metadata
const fileRecord = cachify.files.inspect({ filePath, scope: 'documents' });
console.log(fileRecord);
// Read file content (status: "miss" if loaded from disk, "hit" if cached)
const readResult = await cachify.files.read({ filePath, scope: 'documents' });
console.log(readResult);
// Scopes are optional β caches in default "global" scope
await cachify.files.set(filePath);
```
**Notes:**
- **Scopes are optional**; if omitted, the `"global"` scope is used.
- Scopes allow logical separation of data without polluting record keys (e.g., `"users"`,`"orders"`, `"documents"`).
- `kvs` is the plural API for key-value operations.
- File caching separates **metadata** from **content**, which is loaded on read.
- `storeIn` allows targeting multiple backends for redundancy, failover, or multi-region caching.
### 5. Persistence & Cold Start Recovery
Cachify supports persistent caching via adapters (e.g., local, S3) to backup and restore caches:
```js
import path from 'path';
const backupName = 'cars';
const testFilePath = path.join(process.cwd(), 'test', 'sample.txt');
// Register persistence adapters
cachify.persistence.use('local', { path: process.cwd() });
// Set some records
await cachify.kvs.set('a', 1);
await cachify.files.set(testFilePath);
// Backup cache
await cachify.persistence.backup('local', backupName);
// Clear memory
await cachify.clear();
// Restore cache
await cachify.persistence.restore('local', backupName);
// Access restored data
console.log(await cachify.kvs.get('a')); // 1
const fileResponse = await cachify.files.read({ filePath: testFilePath });
console.log(fileResponse); // { status: 'hit' | 'miss', content: Buffer }
```
**Notes:**
- Backup saves all key-value and file caches to the configured persistence adapter.
- Restore reloads all cached data, enabling fast recovery after cold starts.
- Works with multiple cache flavors and storage backends.
- Cloud adapters (e.g., S3) can be registered similarly to local adapters for persistent storage.
___
## Testing
To run Cachify tests locally, you need to create an environment file at `tests/setup/test.env`
### Required Environment Variables
- **Redis Testing**
- `REDIS_TEST_URL` β The URL of a Redis server for running tests.
- β οΈ The Redis server will be flushed before tests start, so **do not use a production Redis instance**.
- **Amazon S3 Persistence Testing (Optional)**
- `S3_TEST_BUCKET` β The bucket name to use for tests.
- `S3_TEST_REGION` β The AWS region of the bucket.
- `S3_TEST_KEY` β AWS access key ID.
- `S3_TEST_SECRET` β AWS secret access key.
### Running Tests
After creating and populating the env file, simply run:
```bash
npm test
```
___
## Benchmarking
Cachify provides a built-in benchmarking suite to measure the performance of key-value and file caching across different storage engines.
### Environment Setup
You can optionally create an environment file at `benchmarks/benchmarks.env`. This file allows you to customize benchmark parameters. If itβs missing, Cachify will automatically fall back to safe defaults.
### Optional Environment Variables
- `BENCHMARK_KV_COUNT` β Number of key-value records to benchmark. Default: `100000`
- `BENCHMARK_FILES_COUNT` β Number of file records to benchmark. Default: `1000`
- `BENCHMARK_OUT_DIR` β Directory to store benchmark results. Default: `${process.cwd()}/cachify/benchmarks-results`
- `REDIS_BENCHMARK_URL` β Redis connection URL to include Redis in the benchmarks.
If omitted, all benchmarks will run in-memory only.
> [!WARNING]
> **Redis Safety Notice**
> When `REDIS_BENCHMARK_URL` is defined, Cachify will **flush the Redis database** before starting the benchmarks to ensure accurate results.
>
> β οΈ **Never use a production Redis server for benchmarking!**
> Use a dedicated or disposable Redis instance instead, as **all data will be permanently deleted**.
>
### Running Benchmarks
Once ready, simply run:
```bash
npm run benchmark
```
This command runs the full benchmark suite and outputs performance results β including read/write throughput and engine comparison β to the directory defined in `BENCHMARK_OUT_DIR` (or the default if not set).
___
## License
This software is licensed under the **Nasriya Personal & Commercial License (NPCL)**, version 1.0.
Please read the license from [here](https://github.com/nasriyasoftware/Cachify?tab=License-1-ov-file).