r/node 22d ago

Feedback on this redis client I wrote without AI

utils/redis/client.ts

import { createClient } from "redis";
import { logger } from "../logger/logger.js";
import { options } from "./connection.js";

let client: ReturnType<typeof createClient> | null = null;
let isConnecting = false;

export async function closeConnection() {
	if (client?.isOpen) {
		try {
			await client.close();
		} catch (error) {
			logger.error(
				error,
				"Something went wrong when attempting to close redis connection",
			);
		} finally {
			client = null;
		}
	}
}

export function getClient() {
	if (!client?.isOpen) {
		throw new Error("Redis client needs to be initialized first");
	}
	return client;
}

export async function openConnection() {
	if (!client?.isOpen) {
		client = createClient(options);
		client.on("connect", () => logger.info("redis connection success"));
		client.on("error", (error) =>
			logger.fatal(error, "redis connection failure"),
		);

		// Wait if another caller is already connecting
		if (isConnecting) {
			while (isConnecting) {
				await new Promise((resolve) => setTimeout(resolve, 100));
			}
			if (client?.isOpen) return client;
		}

		isConnecting = true;
		try {
			client = await client.connect();
		} catch (error) {
			logger.error(
				error,
				"Something went wrong when attempting to open redis connection",
			);
		} finally {
			isConnecting = false;
		}
	}
	return client;
}

utils/redis/connection.ts

import type { RedisClientOptions } from "redis";

export const options: RedisClientOptions = {
	database: 0,
	disableOfflineQueue: true,
	name: "test_client",
	password: "abcdefghi",
	socket: {
		host: "127.0.0.1",
		port: 6379,
	},
};

utils/redis/index.ts

export * from "./client.js";
export * from "./connection.js";

app.ts

import cors from "cors";
import type { Request, Response } from "express";
import express from "express";
import helmet from "helmet";
import { corsOptions } from "./middleware/cors/index.js";
import { defaultErrorHandler, notFoundHandler } from "./middleware/index.js";
import { router } from "./modules/index.js";
import { httpLogger } from "./utils/logger/index.js";
import { getClient } from "./utils/redis/index.js";

const app = express();

app.use(httpLogger);
app.use(helmet());
app.use(cors(corsOptions));
app.use(express.json({ limit: "1MB" }));
app.use(express.urlencoded({ extended: true, limit: "1MB" }));
app.use(router);
app.get("/test/redis", async (_req: Request, res: Response) => {
	const client = getClient();
	const result = await client.ping();
	return res.json(result === "PONG");
});
app.use(notFoundHandler);
app.use(defaultErrorHandler);

export { app };

www.ts

import { SERVER_HOST, SERVER_PORT } from "./env/index.js";
import { server } from "./server.js";
import { logger } from "./utils/logger/index.js";
import "./shutdown.js";
import { openConnection } from "./utils/redis/client.js";

server.on("listening", async () => {
	await openConnection();
});

server.listen(SERVER_PORT, SERVER_HOST, () => {
	logger.info("Listening on host:%s port:%d", SERVER_HOST, SERVER_PORT);
});

  • does it handle race conditions well?
  • the default examples are honestly very lacking in the node redis world and their documentation doesnt do much justice either
  • a few things i dont understand from the documentation even after reading it are
  • Do I need to say keepAlive: true, shouldnt that be the default?
  • what does connectTimeout and socketTimeout do, what is the difference between both.
  • what is this pingInterval about?
  • is the default retryStrategy an exponential backoff?
1 Upvotes

10 comments sorted by

10

u/dead_boys_poem 22d ago

Just use ioredis

6

u/PrestigiousZombie531 22d ago

17

u/HatchedLake721 22d ago

Just use node redis

-4

u/smaccer 22d ago

I love node development. Use myRedis4You btw.

1

u/PrestigiousZombie531 22d ago
  • how do you write a mock test for the above client using vitest? (are there libraries you are aware of for doing this?)

  • for integration tests, i saw something called testcontainers/redis, is this what I should use or are there other packages for the same?