Skip to content

yammm/ynode-redis

Repository files navigation

@ynode/redis

Copyright (c) 2025 Michael Welter me@mikinho.com

npm version License: MIT

A better Redis Fastify plugin that uses the official Redis library

Why?

A lightweight Fastify plugin that exposes a single node‑redis client (redis package) on your Fastify instance and handles connection lifecycle (connect → ready → reconnect → close) for you.

  • ✅ Uses the official redis client (not ioredis)
  • ✅ Clean Fastify integration with proper startup/shutdown hooks
  • ✅ Simple API: fastify.redis everywhere in your app

If you are looking for the ioredis‑based plugin, see @fastify/redis.

Installation

Install the package and its required peer dependency, redis.

npm install @ynode/redis redis

Basic Usage

import redis from "@ynode/redis";

if (fastify.argv.redis) {
    // connect to redis
    await fastify.register(redis, { url: fastify.argv.redis });
}

Usage

Register the plugin with your Fastify instance. Any options you provide are passed directly to the underlying node-redis createClient method.

import Fastify from "fastify";
import fastifyRedis from "@ynode/redis";

const fastify = Fastify({
    logger: true,
});

// Register the plugin with options
fastify.register(fastifyRedis, {
    url: "redis://localhost:6379",
});

// Access the redis client from the fastify instance
fastify.get("/", async (request, reply) => {
    const value = await fastify.redis.get("mykey");
    return { key: "mykey", value: value };
});

const start = async () => {
    try {
        await fastify.listen({ port: 3000 });
    } catch (err) {
        fastify.log.error(err);
        process.exit(1);
    }
};

start();

Connection Lifecycle

This plugin manages Redis connection lifecycle using Fastify hooks:

  • Connects during Fastify startup (onReady)
  • Closes the Redis client during Fastify shutdown (onClose)

Startup is fail-fast. If Redis cannot be reached (or startup metadata commands fail), fastify.listen() rejects and the server will not start.

Key Namespacing

Use withNamespace(namespace) as the default, concurrency-safe way to scope keys. It returns a scoped client view without mutating global fastify.redis.namespace.

await fastify.register(fastifyRedis, {
    url: "redis://localhost:6379",
});

const tenantRedis = fastify.redis.withNamespace("codex");

await tenantRedis.set("status", "online"); // writes "codex:status"
await tenantRedis.get("status"); // reads "codex:status"

Scoped clients can safely coexist:

const tenantA = fastify.redis.withNamespace("alpha");
const tenantB = fastify.redis.withNamespace("beta");

await tenantA.set("counter", "1"); // alpha:counter
await tenantB.set("counter", "1"); // beta:counter

The mutable fastify.redis.namespace property is still supported for backward compatibility:

fastify.redis.namespace = "klingon";
await fastify.redis.set("status", "battle-ready"); // writes "klingon:status"

If future node-redis internals change in a way that prevents safe namespace interception, this plugin now fails fast at startup with REDIS_NAMESPACE_INCOMPATIBLE_CLIENT instead of silently writing unprefixed keys.

To bypass namespacing for specific operations, use raw (works for base and scoped clients):

await fastify.redis.raw.get("status"); // reads the literal key "status" (no prefix)
await fastify.redis.raw.set("status", "manual"); // writes key "status" (no prefix)

const tenantRedis = fastify.redis.withNamespace("codex");
await tenantRedis.raw.get("status"); // still unprefixed

Advanced flows inherit the scope of the client that creates them:

const tenantRedis = fastify.redis.withNamespace("codex");

const transaction = tenantRedis.multi();
transaction.set("status", "ready").get("status");
await transaction.exec(); // operates on codex:status

const pipeline = tenantRedis.multi();
pipeline.set("counter", "1").get("counter");
await pipeline.execAsPipeline(); // operates on codex:counter

const rawTransaction = tenantRedis.raw.multi();
rawTransaction.set("status", "literal");
await rawTransaction.exec(); // operates on literal "status"

Health and Readiness

This plugin exposes simple probe helpers:

  • fastify.redis.readiness(): lightweight state snapshot
  • fastify.redis.healthcheck(): ping-based health check that never throws
const readiness = fastify.redis.readiness();
// { isOpen: true, isReady: true, namespace: "codex" }

const health = await fastify.redis.healthcheck();
// { ok: true, ping: "PONG", latencyMs: 1, isOpen: true, isReady: true, namespace: "codex" }

Options

Plugin-specific options

  • name (string, optional): connection name used with Redis CLIENT SETNAME. Default: @ynode/redis
  • namespace (string, optional): key prefix for Redis commands that operate on keys. Example: namespace: "codex" prefixes keys as codex:<key>.

Redis client options

All other options are passed directly to the createClient function from the official redis library.

For a full list of available options, please see the official node-redis documentation.

TypeScript

This package ships TypeScript declarations, including Fastify instance augmentation for fastify.redis.

import Fastify from "fastify";
import fastifyRedis from "@ynode/redis";

const app = Fastify();
await app.register(fastifyRedis, { url: "redis://localhost:6379" });

await app.redis.set("health", "ok");

Testing and CI

  • npm test runs project linting, unit tests, and integration tests.
  • Integration tests use REDIS_URL when provided.
  • If REDIS_URL is not set, tests try to start a local redis-server automatically.
  • CI runs on push and pull request, starts a Redis service, and executes npm test.

License

This project is licensed under the MIT License.

About

A better Redis Fastify plugin that uses the official Redis library

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors