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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@owlmeans/redis-resource

v0.1.1

Published

The **@owlmeans/redis-resource** package provides Redis-based resource storage implementation for OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorizati

Readme

@owlmeans/redis-resource

The @owlmeans/redis-resource package provides Redis-based resource storage implementation for OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorization.

Purpose

This package serves as a Redis integration layer for the OwlMeans resource system that:

  • Provides Redis-backed storage for resources with high performance and scalability
  • Supports pub/sub messaging for real-time data synchronization across services
  • Implements full CRUD operations with Redis-optimized performance
  • Offers TTL support for automatic expiration of records
  • Enables horizontal scaling through Redis clustering
  • Integrates with context system for dependency injection and service management

Core Concepts

Redis Resource

A Redis resource is a storage implementation that uses Redis as the backend, providing fast key-value storage with advanced features like pub/sub, TTL, and clustering support.

Key Management

Records are stored in Redis using a configurable prefix and key structure, allowing for namespace separation and efficient data organization.

Pub/Sub Integration

The package provides built-in pub/sub functionality for real-time data synchronization, enabling reactive updates across distributed systems.

Database Service Integration

Integrates with the OwlMeans database service pattern, allowing for connection pooling and database management.

API Reference

Factory Functions

makeRedisResource<R, T>(alias, dbAlias?, serviceAlias?, makeCustomResource?): T

Creates a new Redis resource instance.

import { makeRedisResource } from '@owlmeans/redis-resource'

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

const userResource = makeRedisResource<User>('users', 'redis-main')

Parameters:

  • alias: string - Resource alias for context registration
  • dbAlias: string (optional) - Database alias, defaults to DEFAULT_DB_ALIAS ('redis')
  • serviceAlias: string (optional) - Service alias, defaults to DEFAULT_DB_ALIAS
  • makeCustomResource: ResourceMaker (optional) - Custom resource factory function

Returns: RedisResource instance with full CRUD and pub/sub capabilities

Resource Methods

The Redis resource implements the standard Resource<ResourceRecord> interface with additional Redis-specific methods:

Core CRUD Operations

get<T>(id, field?, opts?): Promise<T>

Retrieves a record by ID or field value. Throws UnknownRecordError if not found.

const user = await userResource.get<User>('user-123')

load<T>(id, field?, opts?): Promise<T | null>

Loads a record by ID or field value. Returns null if not found.

const user = await userResource.load<User>('user-123')
if (user) {
  console.log('User found:', user.name)
}

create<T>(record, opts?): Promise<T>

Creates a new record in Redis storage.

const user = await userResource.create({
  id: 'user-123',
  name: 'John Doe',
  email: '[email protected]'
}, { ttl: 3600 }) // TTL in seconds

Options:

  • ttl: number | Date - Time to live in seconds (number) or expiration date (Date)

update<T>(record, opts?): Promise<T>

Updates an existing record.

const updatedUser = await userResource.update({
  id: 'user-123',
  name: 'John Smith',
  email: '[email protected]'
}, { ttl: 7200 })

save<T>(record, opts?): Promise<T>

Creates or updates a record (upsert operation).

const user = await userResource.save({
  id: 'user-123',
  name: 'John Doe',
  email: '[email protected]'
})

delete(id, opts?): Promise<T | null>

Deletes a record and returns it, or null if not found.

const deletedUser = await userResource.delete('user-123')

pick<T>(id, opts?): Promise<T>

Deletes a record and returns it. Throws UnknownRecordError if not found.

const pickedUser = await userResource.pick('user-123')

list<T>(criteria?, opts?): Promise<ListResult<T>>

Lists records with pagination support.

const result = await userResource.list<User>({}, { 
  pager: { page: 0, size: 20 } 
})
console.log('Users:', result.items)
console.log('Total:', result.pager?.total)

Redis-Specific Methods

key(key?): string

Generates a Redis key with the configured prefix.

const redisKey = userResource.key('user-123')
console.log(redisKey) // "prefix:user-123"

subscribe<Type>(handler, key?): Promise<() => Promise<void>>

Subscribes to Redis pub/sub messages for real-time updates.

const unsubscribe = await userResource.subscribe<User>(
  async (user) => {
    console.log('User updated:', user)
  },
  'user-updates'
)

// Later, unsubscribe
await unsubscribe()

Parameters:

  • handler: (value: Type) => Promise - Message handler function
  • key: SubOpts (optional) - Subscription options or key

publish<Type>(value, key?): Promise<void>

Publishes a message to Redis pub/sub.

await userResource.publish<User>({
  id: 'user-123',
  name: 'John Doe',
  email: '[email protected]'
}, 'user-updates')

Types

RedisResource<T>

Main interface for Redis resources extending the base Resource interface.

interface RedisResource<T extends ResourceRecord> extends Resource<T> {
  name?: string
  db: RedisDb
  key: (key?: string) => string
  subscribe: <Type extends T>(handler: (value: Type) => Promise<void>, key?: SubOpts) => Promise<() => Promise<void>>
  publish: <Type extends T>(value: Type, key?: string) => Promise<void>
}

RedisDb

Redis database connection interface.

interface RedisDb {
  client: RedisCommander
  prefix: string
}

RedisDbService

Service interface for Redis database management.

interface RedisDbService extends ResourceDbService<RedisDb, RedisClient> {}

RedisClient

Type alias for Redis client (supports both single Redis and Cluster).

type RedisClient = Redis | Cluster

SubscriptionOptions

Options for Redis subscriptions.

interface SubscriptionOptions extends LifecycleOptions {
  key?: string
  once?: boolean
}

Constants

DEFAULT_DB_ALIAS

Default database alias for Redis connections.

const DEFAULT_DB_ALIAS = 'redis'

DEFAULT_PAGE_SIZE

Default page size for list operations.

const DEFAULT_PAGE_SIZE = 10

Usage Examples

Basic Usage

import { makeRedisResource } from '@owlmeans/redis-resource'

interface Product {
  id: string
  name: string
  price: number
  category: string
}

// Create Redis resource
const productResource = makeRedisResource<Product>('products')

// Create products
await productResource.create({
  id: 'prod-1',
  name: 'Laptop',
  price: 999.99,
  category: 'electronics'
})

await productResource.create({
  id: 'prod-2',
  name: 'Book',
  price: 19.99,
  category: 'books'
}, { ttl: 86400 }) // Expires in 24 hours

// Retrieve products
const laptop = await productResource.get('prod-1')
console.log('Product:', laptop.name, laptop.price)

// List all products
const { items, pager } = await productResource.list()
console.log('Total products:', pager?.total)

Context Integration

import { makeRedisResource } from '@owlmeans/redis-resource'
import { makeServerContext, makeServerConfig } from '@owlmeans/server-context'
import { AppType } from '@owlmeans/context'

// Create server context
const config = makeServerConfig(AppType.Backend, 'product-service')
const context = makeServerContext(config)

// Create and register Redis resource
const productResource = makeRedisResource<Product>('products', 'redis-main')
context.registerResource(productResource)

// Configure and initialize context
context.configure()
await context.init()

// Use resource through context
const resource = context.resource<typeof productResource>('products')

Pub/Sub Usage

import { makeRedisResource } from '@owlmeans/redis-resource'

const orderResource = makeRedisResource<Order>('orders')

// Subscribe to order updates
const unsubscribe = await orderResource.subscribe<Order>(
  async (order) => {
    console.log('Order updated:', order.id, order.status)
    
    // Process order update
    if (order.status === 'completed') {
      await processCompletedOrder(order)
    }
  },
  { key: 'order-updates', once: false }
)

// Publish order updates
await orderResource.publish({
  id: 'order-123',
  status: 'completed',
  total: 299.99
}, 'order-updates')

// Clean up subscription
await unsubscribe()

TTL and Expiration

const sessionResource = makeRedisResource<Session>('sessions')

// Create session with TTL
await sessionResource.create({
  id: 'session-abc123',
  userId: 'user-456',
  createdAt: new Date()
}, { ttl: 3600 }) // Expires in 1 hour

// Update with new expiration
await sessionResource.update({
  id: 'session-abc123',
  lastActivity: new Date()
}, { ttl: new Date(Date.now() + 7200000) }) // Expires in 2 hours

Real-time Synchronization

// Service A - Updates user data
const userResource = makeRedisResource<User>('users')

await userResource.update({
  id: 'user-123',
  lastLogin: new Date(),
  status: 'online'
})

// Notify other services
await userResource.publish({
  id: 'user-123',
  event: 'status-change',
  status: 'online'
}, 'user-events')

// Service B - Listens for user events
const unsubscribe = await userResource.subscribe<UserEvent>(
  async (event) => {
    console.log('User event:', event.event, event.id)
    await updateLocalCache(event)
  },
  'user-events'
)

Error Handling

import { UnknownRecordError, RecordExists, MisshapedRecord } from '@owlmeans/resource'

const userResource = makeRedisResource<User>('users')

try {
  // This will throw UnknownRecordError
  await userResource.get('non-existent-user')
} catch (error) {
  if (error instanceof UnknownRecordError) {
    console.log('User not found:', error.message)
  }
}

try {
  await userResource.create({ id: 'user-1', name: 'Alice', email: '[email protected]' })
  // This will throw RecordExists
  await userResource.create({ id: 'user-1', name: 'Bob', email: '[email protected]' })
} catch (error) {
  if (error instanceof RecordExists) {
    console.log('User already exists:', error.message)
    // Use save() for upsert instead
    await userResource.save({ id: 'user-1', name: 'Bob', email: '[email protected]' })
  }
}

Advanced Filtering and Pagination

const productResource = makeRedisResource<Product>('products')

// List with pagination
const page1 = await productResource.list({}, { 
  pager: { page: 0, size: 10 } 
})

const page2 = await productResource.list({}, { 
  pager: { page: 1, size: 10 } 
})

console.log('Page 1:', page1.items.length)
console.log('Page 2:', page2.items.length)
console.log('Total products:', page1.pager?.total)

Integration Patterns

Microservices Architecture

// Product Service
const productResource = makeRedisResource<Product>('products', 'redis-products')

// Order Service  
const orderResource = makeRedisResource<Order>('orders', 'redis-orders')

// Shared event bus
const eventResource = makeRedisResource<Event>('events', 'redis-events')

// Cross-service communication
await productResource.subscribe<ProductEvent>(
  async (event) => {
    if (event.type === 'price-updated') {
      await orderResource.publish({
        type: 'product-price-changed',
        productId: event.productId,
        newPrice: event.price
      }, 'order-events')
    }
  },
  'product-events'
)

Caching Layer

// Use Redis resource as a caching layer
const cacheResource = makeRedisResource<CacheEntry>('cache')

const getCachedData = async <T>(key: string, fetcher: () => Promise<T>): Promise<T> => {
  try {
    const cached = await cacheResource.load(key)
    if (cached && cached.expiresAt > Date.now()) {
      return cached.data as T
    }
  } catch (error) {
    // Cache miss, continue to fetch
  }
  
  const data = await fetcher()
  
  // Cache for 5 minutes
  await cacheResource.save({
    id: key,
    data,
    expiresAt: Date.now() + 300000
  }, { ttl: 300 })
  
  return data
}

// Usage
const userData = await getCachedData(`user:${userId}`, () => fetchUserFromDB(userId))

Performance Considerations

Connection Management

  • Connection Pooling - Use Redis connection pools for high-traffic applications
  • Cluster Support - Leverage Redis clustering for horizontal scaling
  • Pipeline Operations - Batch multiple operations for better performance

Data Organization

  • Key Prefixes - Use meaningful prefixes to organize data and avoid conflicts
  • TTL Management - Set appropriate TTL values to manage memory usage
  • Pagination - Use pagination for large datasets to avoid memory issues

Best Practices

  1. Use appropriate TTL - Set TTL for temporary data to prevent memory bloat
  2. Monitor Redis memory - Keep track of Redis memory usage and key counts
  3. Implement error handling - Handle Redis connection failures gracefully
  4. Use pub/sub wisely - Avoid creating too many subscriptions that could impact performance
  5. Batch operations - Use pipelines for multiple operations when possible

Error Handling

The package uses standard error types from @owlmeans/resource:

  • UnknownRecordError - Thrown when trying to access a non-existent record
  • RecordExists - Thrown when trying to create a record with an existing ID
  • MisshapedRecord - Thrown when record structure is invalid
  • UnsupportedArgumentError - Thrown when unsupported arguments are provided

Additional Redis-specific errors may be thrown by the underlying ioredis client.

Dependencies

This package depends on:

  • @owlmeans/context - For contextual resource management
  • @owlmeans/resource - For resource interface and error types
  • ioredis - Redis client library for Node.js

Related Packages