TypeScript-first Redis data layer

Schema-driven Redis cachingto scale your entities

A TypeScript-first Redis data layer for Node.js with normalization, atomic concurrent writes, ZSET-backed paginated lists, automatic relationship hydration, and built-in resilience.

TypeScript
Native generics
Atomic
Lua-script writes
ZSET
Indexed lists
MIT
License
index.ts
import { RedisGraphCache } from 'redis-graph-cache';

const schema = {
  post: {
    type: 'entity' as const,
    id: 'id',
    key: (id) => `post:${id}`,
    fields: {
      title: { type: 'string' as const },
      content: { type: 'string' as const },
      published: { type: 'boolean' as const },
    },
    relations: {
      author:   { type: 'user',     kind: 'one' as const },
      category: { type: 'category', kind: 'one' as const },
    },
    ttl: 3600,
  },
  user: {
    type: 'entity' as const,
    id: 'id',
    key: (id) => `user:${id}`,
    fields: {
      name:  { type: 'string' as const },
      email: { type: 'string' as const },
    },
    ttl: 7200,
  },
  category: {
    type: 'entity' as const,
    id: 'id',
    key: (id) => `category:${id}`,
    fields: {
      name: { type: 'string' as const },
      slug: { type: 'string' as const },
    },
    ttl: 86400,
  },
};

const cache = new RedisGraphCache(schema, {
  redis: { host: 'localhost', port: 6379 },
});

// Write ONE nested object...
await cache.writeEntity('post', {
  id: 1,
  title: 'Hello World',
  content: 'Graph caching for Redis.',
  published: true,
  author:   { id: 9, name: 'Ada', email: 'ada@example.com' },
  category: { id: 3, name: 'Tech', slug: 'tech' },
});

// ...three keys land in Redis, each with its own TTL.
// Reads recompose the graph:
const post = await cache.readEntity('post', 1);
// post.author.name   === 'Ada'
// post.category.slug === 'tech'
redis-cli
# One write produced three normalized keys.
# Nested objects became id references — not duplicated blobs.

127.0.0.1:6379> KEYS *
1) "post:1"
2) "user:9"
3) "category:3"

127.0.0.1:6379> GET post:1
{"id":1,"title":"Hello World",
 "content":"Graph caching for Redis.",
 "published":true,
 "__rse_authorId":9,
 "__rse_categoryId":3}

127.0.0.1:6379> GET user:9
{"id":9,"name":"Ada","email":"ada@example.com"}

127.0.0.1:6379> GET category:3
{"id":3,"name":"Tech","slug":"tech"}

# Each key carries its own TTL from the schema.
127.0.0.1:6379> TTL post:1      → 3600
127.0.0.1:6379> TTL user:9      → 7200
127.0.0.1:6379> TTL category:3  → 86400

# readEntity('post', 1) rehydrates the full graph
# — no duplicated data, no stale copies, no N+1 round trips.
Features

Why redis-graph-cache?

Caching nested objects in Redis is harder than it looks. We solve the hard problems so you don't have to.

Schema-Driven Storage

Declare entities and relationships once. Auto-validates at boot and handles normalization, hydration, and key generation.

Atomic Lua Scripts

Every read-modify-write path is implemented as a single Lua script. No race conditions, ever.

Cascade Invalidation

One call removes an entity from all lists tracking it. Automatic relationship management.

ZSET-Backed Lists

Paginated reads, sorting by any field, atomic add/remove, size caps, and built-in pagination.

TypeScript Native

Full generic typing with keyof TSchema everywhere. Type-safe API from schema definition.

Built-in Resilience

Circuit breaker, exponential backoff retries, real metrics, and production safety guards.

Quick Installation

Get started in seconds

One command. That's all it takes.

📦
npm
npm add redis-graph-cache ioredis
🧶
yarn
yarn add redis-graph-cache ioredis
pnpm
pnpm add redis-graph-cache ioredis

Ready to cache smarter?

Start building with redis-graph-cache today.

Read the Documentation