@joystick.js/node

Cache

A utility for creating and managing caches in Joystick apps with Redis support and TTL/LRU policies.

The cache() function in @joystick.js/node provides a simple API for managing caches on the server in your app. This utility is useful for temporarily storing data (e.g., frequently accessed records or ephemeral data) that you want to avoid repeatedly fetching or recalculating during runtime.

Caches automatically use Redis as the storage backend if Redis is configured with cache: true in your app's settings, otherwise they fall back to in-memory storage. Both backends support TTL (Time To Live) and LRU (Least Recently Used) eviction policies.

Caches can be defined anywhere on your server, however, the recommended approach is to store each cache definition as a single file in the caches folder at the root of your project and import them into your index.server.js file to pass to the caches() function passed to your joystick.app() via its options object.

Example Usage

Defining a cache

caches/example.js

import { cache } from '@joystick.js/node';

const users_cache = cache('users');

To define a cache, we import the named export cache from @joystick.js/node and then call it, passing the name of our cache(). In response, we get a cache instance with a few different methods for managing the cache.

Setting a cache

caches/example.js

import { cache } from '@joystick.js/node';

const users_cache = cache('users');

users_cache.set([
  { id: '1', name: 'Alice', role: 'admin' },
  { id: '2', name: 'Bob', role: 'editor' },
]);

Above, to set a default value for a cache (or overwrite the entirety of an existing cache), we can use the cache.set() method. To it, we pass an array of objects representing the new value we want to set for the cache.

Finding one or all items in a cache

caches/example.js

import { cache } from '@joystick.js/node';

const users_cache = cache('users');

// Find all users
const all_users = users_cache.find();

// Find users with role 'editor'
const editors = users_cache.find(['role', 'editor']);

// Find a single user by ID
const user = users_cache.find_one(['id', '2']);

Above, we call .find() on a cache to return the full, current cache. To find items matching a specific query, we can call .find() passing an array where the first element is the name of the field on the cache item we want to match and the second element is the value for the field on the cache itme we want to match. If we want a single, specific item, we can use the .find_one() method, using the same array-based querying.

Updating a cache

caches/example.js

import { cache } from '@joystick.js/node';

const users_cache = cache('users');

users_cache.update(['id', '2'], { role: 'superadmin' });

Above, we update a specific item in the cache, first passing an array-style query like we use with the .find() method and then as the second argument, an object containing the modifications we'd like to make to the cache item matching the query passed as the first argument.

Removing items from a cache

caches/example.js

import { cache } from '@joystick.js/node';

const users_cache = cache('users');

users_cache.remove(['id', '1']);

To remove a specific item(s) from the cache, again, we deploy the array-style query syntax we used with the other methods above. Note: this will remove any cache items matching the query, not just the first one to match.

Redis Support

Caches automatically use Redis as the storage backend when Redis is configured in your app's settings with cache: true. This provides several benefits:

  • Shared across processes: Multiple app instances can share the same cache
  • Persistent: Cache survives app restarts
  • Efficient: Uses Redis native data structures for optimal performance

Configuring Redis for caching

settings.development.json

{
  "config": {
    "databases": [
      {
        "provider": "redis",
        "cache": true,
        "options": {}
      }
    ]
  }
}

TTL and LRU Policies

Both in-memory and Redis caches support Time To Live (TTL) and Least Recently Used (LRU) eviction policies:

TTL (Time To Live)

Items automatically expire after the specified time:

caches/session.js

import { cache } from '@joystick.js/node';

// Items expire after 5 minutes (300 seconds)
const session_cache = cache('sessions', { ttl: 300 });

session_cache.add({ user_id: '123', token: 'abc...' });
// Item will be automatically removed after 5 minutes

LRU (Least Recently Used)

Cache maintains a maximum number of items, removing the least recently accessed:

caches/users.js

import { cache } from '@joystick.js/node';

// Keep maximum 1000 items, remove oldest when exceeded
const user_cache = cache('users', { max_items: 1000 });

user_cache.add({ id: '1', name: 'Alice' });
// When 1001st item is added, least recently used item is removed

Combining TTL and LRU

caches/api_responses.js

import { cache } from '@joystick.js/node';

// Items expire after 10 minutes AND keep max 500 items
const api_cache = cache('api_responses', { 
  ttl: 600,        // 10 minutes
  max_items: 500   // Maximum 500 items
});

api_cache.add({ endpoint: '/users', data: [...] });
// Item will be removed after 10 minutes OR when LRU limit is exceeded

How LRU tracking works

  • Items are marked as "accessed" when retrieved via find() or find_one()
  • Items are marked as "accessed" when updated via update()
  • When max_items is exceeded, the least recently accessed items are removed first
  • Redis: Uses sorted sets for efficient LRU tracking
  • In-memory: Uses timestamps for LRU tracking

API

cache()

cache(cache_name: string, options?: object) => object

Parameters

cache_name string required
A string to uniquely identify the cache in process.caches. Used as the key to store and retrieve cache data.
options object
Optional configuration object for TTL and LRU policies.

cache.add()

cache.add(cache_item: object) => void

Parameters

cache_item object required
The object to add to the cache array.

cache.find()

cache.find(query_array: [string, any]) => array

Parameters

query_array array
An array with two elements: [key, value]. Only cache items matching this key/value pair will be returned. If omitted, all items in the cache are returned.

cache.find_one()

cache.find_one(query_array: [string, any]) => object|null

Parameters

query_array array
An array with two elements: [key, value]. Only the first cache item matching this key/value pair will be returned. If omitted, returns null.

cache.set()

cache.set(cache_array: array) => void

Parameters

cache_array array required
An array of objects to set as the entire cache contents. Overwrites any existing cache data.

cache.update()

cache.update(match_array: [string, any], replacement_item: object) => void

Parameters

match_array array required
An array with two elements: [key_to_match, value_to_match]. Identifies the cache item to update.
replacement_item object required
The object to merge into the matched cache item.

cache.remove()

cache.remove(match_array: [string, any]) => void

Parameters

match_array array required
An array with two elements: [key_to_match, value_to_match]. Identifies the cache items to remove.