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

@fluojs/redis

v1.1.0

Published

App-scoped Redis lifecycle management, raw client injection, and RedisService facade for Fluo.

Downloads

301

Readme

@fluojs/redis

Shared Redis connection layer for fluo. It provides a default app-scoped ioredis client plus optional named clients, all managed by the application lifecycle.

Table of Contents

Installation

npm install @fluojs/redis ioredis

When to Use

  • When you need a shared Redis connection across multiple modules (caching, queues, throttlers).
  • When you want automatic connection management (connect on bootstrap, quit on shutdown).
  • When you need a JSON-aware facade for common key-value operations.

Quick Start

Register the Module

Use RedisModule.forRoot(options) to register the default Redis client and RedisService facade.

RedisModule.forRoot(...) is intentionally synchronous and always creates a new ioredis client from Redis constructor options; it does not adopt an externally constructed client. When migrating from NestJS async dynamic modules such as forRootAsync(...), resolve secrets, environment-specific hosts, and TLS options at the application boundary first, then pass the final Redis options into forRoot(...). Keep externally constructed clients outside this module and close them from the application lifecycle. fluo does not defer Redis module wiring to an async factory hidden inside the module graph.

import { Module } from '@fluojs/core';
import { RedisModule } from '@fluojs/redis';

@Module({
  imports: [
    RedisModule.forRoot({
      host: 'localhost',
      port: 6379,
    }),
  ],
})
export class AppModule {}

Use the Redis Service

Inject RedisService for high-level operations or REDIS_CLIENT for the raw ioredis instance.

RedisService.get() attempts JSON parsing and falls back to the raw string; missing keys return null. RedisService.set() serializes values with JSON.stringify(): finite positive integer TTLs use Redis EX, while finite positive fractional TTLs use PX with milliseconds rounded up. Omit the TTL or pass a non-positive or non-finite value for a persistent key.

import { Inject } from '@fluojs/core';
import { RedisService } from '@fluojs/redis';

@Inject(RedisService)
export class CacheRepository {
  constructor(private readonly redis: RedisService) {}

  async saveUser(id: string, user: object) {
    await this.redis.set(`user:${id}`, user, 3600);
  }

  async getUser(id: string) {
    return await this.redis.get(`user:${id}`);
  }
}

Common Patterns

Lifecycle Ownership

Every RedisModule.forRoot(...) registration creates a new client that @fluojs/redis owns, including named clients registered through RedisModule.forRoot({ name, ... }). The module never adopts an existing client instance.

  • Fluo always forces lazyConnect: true, even if callers cast options manually, so sockets open during application bootstrap instead of import time.
  • During bootstrap, the lifecycle service only calls connect() while the client is still in ioredis wait state.
  • Lifecycle-owned connect() and quit() calls are bounded by package timeouts (10_000 ms by default) so bootstrap and shutdown do not wait forever on a stalled Redis command. Override them with lifecycle.connectTimeoutMs and lifecycle.quitTimeoutMs; pass 0 only when the host process intentionally owns an unbounded wait.
  • During shutdown, ready/connecting clients attempt quit() first for graceful teardown, while monitoring, wait, and closed-transition states use disconnect() directly.
  • If quit() fails, Fluo falls back to disconnect() and only rethrows when the client still remains open afterward.

Named Clients

Use RedisModule.forRoot({ name, ...options }) when one application needs more than one Redis connection. RedisModule.forRoot(options) without name provides the default REDIS_CLIENT and RedisService aliases, and named registrations are resolved with getRedisClientToken(name) and getRedisServiceToken(name).

  • Omit name when you want the default aliases: REDIS_CLIENT / RedisService.
  • Pass name when you want the named helpers: getRedisClientToken(name) / getRedisServiceToken(name).
  • name identifies the Fluo registration only. For an ioredis Sentinel master name, pass sentinelName; Fluo forwards it to ioredis as its constructor name option without changing the registration tokens.
  • Named clients follow the same bootstrap/shutdown contract as the default client; only the default registration exports the REDIS_CLIENT / RedisService aliases.
  • Names are trimmed, and blank or whitespace-only names are rejected by token/component helpers.
import { Module, Inject } from '@fluojs/core';
import type Redis from 'ioredis';
import {
  getRedisClientToken,
  getRedisServiceToken,
  RedisModule,
  RedisService,
} from '@fluojs/redis';

const ANALYTICS_REDIS = getRedisServiceToken('analytics');
const ANALYTICS_REDIS_CLIENT = getRedisClientToken('analytics');

@Module({
  imports: [
    RedisModule.forRoot({ host: 'localhost', port: 6379 }),
    RedisModule.forRoot({ name: 'analytics', host: 'localhost', port: 6380 }),
    RedisModule.forRoot({ name: 'sentinel-cache', sentinelName: 'mymaster', sentinels: [{ host: 'localhost', port: 26379 }] }),
  ],
})
export class AppModule {}

@Inject(RedisService, ANALYTICS_REDIS, ANALYTICS_REDIS_CLIENT)
export class AnalyticsStore {
  constructor(
    private readonly defaultRedis: RedisService,
    private readonly analyticsRedis: RedisService,
    private readonly analyticsClient: Redis,
  ) {}
}

Raw Client Access

If you need advanced Redis commands (pipelines or Lua scripts), inject the raw client directly.

If you already injected RedisService, call redis.getRawClient() to access the same underlying ioredis instance.

Redis Pub/Sub is the exception to ordinary shared-client reuse: Redis switches subscribed connections into subscribe mode, so do not use the lifecycle-managed REDIS_CLIENT or a RedisService.getRawClient() result as both publisher and subscriber. Create a dedicated subscriber connection with client.duplicate() (or an explicitly named RedisModule.forRoot({ name: 'subscriber', ... }) registration) and let that connection have its own lifecycle owner.

If you use client.duplicate(), the duplicate is application-owned: connect it, subscribe with it, and close it from your own shutdown path. If you want fluo to own subscriber startup/shutdown timeouts, prefer a named registration and inject it with getRedisClientToken(name).

import { Inject } from '@fluojs/core';
import { REDIS_CLIENT } from '@fluojs/redis';
import type Redis from 'ioredis';

@Inject(REDIS_CLIENT)
export class AdvancedService {
  constructor(private readonly client: Redis) {}

  async executeComplex() {
    return await this.client.pipeline().set('foo', 'bar').get('foo').exec();
  }
}
import { Inject, Module } from '@fluojs/core';
import { getRedisClientToken, RedisModule } from '@fluojs/redis';
import { RedisPubSubMicroserviceTransport } from '@fluojs/microservices';
import type Redis from 'ioredis';

const COMMAND_REDIS = getRedisClientToken();
const SUBSCRIBER_REDIS = getRedisClientToken('subscriber');

@Module({
  imports: [
    RedisModule.forRoot({ host: 'localhost', port: 6379 }),
    RedisModule.forRoot({ name: 'subscriber', host: 'localhost', port: 6379 }),
  ],
})
export class RedisConnectionsModule {}

@Inject(COMMAND_REDIS, SUBSCRIBER_REDIS)
export class PubSubTransportFactory {
  constructor(
    private readonly commandClient: Redis,
    private readonly subscriberClient: Redis,
  ) {}

  createTransport() {
    return new RedisPubSubMicroserviceTransport({
      publishClient: this.commandClient,
      subscribeClient: this.subscriberClient,
    });
  }
}

Public API Overview

Core

  • RedisModule: Registers Redis clients and lifecycle hooks.
  • RedisModule.forRoot(options): Registers the default Redis client plus RedisService facade when name is omitted, or an additional named Redis client when name is provided. The default registration is global by default; named registrations are scoped. Use lifecycle.connectTimeoutMs and lifecycle.quitTimeoutMs to bound Fluo-owned connection startup and shutdown.
  • Lifecycle hooks are configured only through RedisModule.forRoot(...).lifecycle; the internal lifecycle service is intentionally not part of the public API.
  • RedisService: Facade with JSON codec support and get/set/del methods.
  • REDIS_CLIENT: DI token for the underlying ioredis instance.
  • DEFAULT_REDIS_CLIENT_NAME: Stable default Redis client name.
  • getRedisClientToken(name): DI token helper for a named raw client. Omitting name returns the default REDIS_CLIENT token.
  • getRedisServiceToken(name): DI token helper for a named RedisService facade. Omitting name returns the default RedisService token.
  • getRedisComponentId(name): Status/dependency id helper used by Redis-consuming packages (redis.default, redis.cache, etc.).
  • createRedisPlatformStatusSnapshot(input): Adapts Redis connection state into Fluo's platform health/readiness snapshot contract.

Types

  • DefaultRedisModuleOptions: Options accepted by the unnamed default Redis registration, including optional global alias visibility and lifecycle timeout controls.
  • NamedRedisModuleOptions: Options accepted by additional named Redis registrations, including required name and scoped lifecycle timeout controls.
  • RedisModuleOptions: Configuration options passed to the ioredis constructor after Fluo removes module-only name, global, lifecycle, and sentinelName fields. sentinelName is forwarded as the ioredis Sentinel master name, while name remains the Fluo registration identifier.
  • RedisClientOptions: Redis constructor options after Fluo removes module-only fields and before it forces lazyConnect: true internally.
  • RedisLifecycleOptions: Optional timeout controls for Fluo-owned connect() and quit() lifecycle commands.
  • PersistencePlatformStatusSnapshot, RedisStatusAdapterInput: Status snapshot input/output types.

Related Packages

  • @fluojs/cache-manager: Uses this package for Redis-backed caching.
  • @fluojs/queue: Uses this package for distributed job processing.
  • @fluojs/throttler: Uses this package for distributed rate limiting.

Example Sources

  • packages/redis/src/module.test.ts: Module lifecycle and DI wiring.
  • packages/redis/src/public-api.test.ts: Root-barrel export guard for the documented Redis surface.
  • packages/redis/src/redis-service.ts: Facade implementation and codec logic.