Skip to content

Commit 5b20f74

Browse files
Merge pull request #26 from zulfikar-ditya/refactor/bun-redis-client
refactor: replace ioredis with Bun's built-in RedisClient
2 parents be1afd8 + 248aa43 commit 5b20f74

9 files changed

Lines changed: 59 additions & 62 deletions

File tree

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ Dockerfile
2424
docker-compose.yml
2525

2626
.agents
27-
.claude
27+
.claude
28+
CLAUDE.md

bun.lock

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@
4343
"hono-pino": "^0.10.3",
4444
"hono-rate-limiter": "^0.5.3",
4545
"hono-zod-openapi": "^1.1.1",
46-
"ioredis": "^5.10.1",
4746
"nodemailer": "^8.0.7",
4847
"pg": "^8.21.0",
4948
"pino": "^10.3.1"
5049
},
5150
"devDependencies": {
51+
"@eslint/js": "^10.0.1",
5252
"@types/bcryptjs": "^3.0.0",
5353
"@types/bun": "^1.3.14",
5454
"@types/crypto-js": "^4.2.2",
@@ -63,6 +63,7 @@
6363
"eslint-plugin-import": "^2.32.0",
6464
"eslint-plugin-prettier": "^5.5.5",
6565
"eslint-plugin-simple-import-sort": "^13.0.0",
66+
"globals": "^17.6.0",
6667
"husky": "^9.1.7",
6768
"lint-staged": "^17.0.5",
6869
"prettier": "^3.8.3",

src/bull/queue/send-email.queue.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Queue } from "bullmq";
22
import { RedisClient } from "@database";
33

4-
const queueRedis = RedisClient.getQueueRedisClient();
5-
64
export const sendEmailQueue = new Queue("send-email", {
7-
connection: queueRedis,
5+
connection: RedisClient.getQueueConnection(),
86
});

src/bull/worker/send-email.worker.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { RedisClient } from "@database";
33
import { logger } from "@utils";
44
import { EmailOptions, EmailService } from "@mail/mail.service";
55

6-
const queueRedis = RedisClient.getQueueRedisClient();
7-
86
const worker = new Worker<EmailOptions>(
97
"send-email",
108
async (job) => {
@@ -17,7 +15,7 @@ const worker = new Worker<EmailOptions>(
1715
}
1816
},
1917
{
20-
connection: queueRedis,
18+
connection: RedisClient.getQueueConnection(),
2119
},
2220
);
2321

src/libs/cache/cache.ts

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,15 @@
1-
import Redis from "ioredis";
1+
import type { RedisClient as BunRedisClient } from "bun";
22
import { logger } from "@utils";
33
import { RedisClient } from "@database";
44

55
class Cache {
6-
private static redis: Redis | null = null;
7-
8-
private static getRedisClient(): Redis {
9-
if (!this.redis) {
10-
this.redis = RedisClient.getRedisClient();
11-
}
12-
13-
return this.redis;
6+
private static getClient(): BunRedisClient {
7+
return RedisClient.getRedisClient();
148
}
159

1610
static async get<T>(key: string): Promise<T | null> {
1711
try {
18-
const client = this.getRedisClient();
19-
const value = await client.get(key);
12+
const value = await this.getClient().get(key);
2013
return value ? (JSON.parse(value) as T) : null;
2114
} catch (error) {
2215
logger.error(error, `Error getting cache for key ${key}:`);
@@ -30,36 +23,37 @@ class Cache {
3023
ttl: number = 3600,
3124
): Promise<void> {
3225
try {
33-
const client = this.getRedisClient();
34-
await client.set(key, JSON.stringify(value), "EX", ttl);
26+
await this.getClient().send("SET", [
27+
key,
28+
JSON.stringify(value),
29+
"EX",
30+
String(ttl),
31+
]);
3532
} catch (error) {
3633
logger.error(error, `Error setting cache for key ${key}:`);
3734
}
3835
}
3936

4037
static async delete(key: string): Promise<void> {
4138
try {
42-
const client = this.getRedisClient();
43-
await client.del(key);
39+
await this.getClient().del(key);
4440
} catch (error) {
4541
logger.error(error, `Error deleting cache for key ${key}:`);
4642
}
4743
}
4844

4945
static async flush(): Promise<void> {
5046
try {
51-
const client = this.getRedisClient();
52-
await client.flushdb();
47+
await this.getClient().send("FLUSHDB", []);
5348
} catch (error) {
5449
logger.error(error, "Error flushing Redis cache:");
5550
}
5651
}
5752

5853
static async exists(key: string): Promise<boolean> {
5954
try {
60-
const client = this.getRedisClient();
61-
const exists = await client.exists(key);
62-
return exists === 1;
55+
const exists = await this.getClient().exists(key);
56+
return exists === true;
6357
} catch (error) {
6458
logger.error(error, `Error checking existence of key ${key}:`);
6559
return false;
@@ -87,21 +81,17 @@ class Cache {
8781

8882
static async getKeys(pattern: string): Promise<string[]> {
8983
try {
90-
const client = this.getRedisClient();
91-
const keys = await client.keys(pattern);
84+
const keys = await this.getClient().keys(pattern);
9285
return keys;
9386
} catch (error) {
9487
logger.error(error, `Error getting keys with pattern ${pattern}:`);
9588
return [];
9689
}
9790
}
9891

99-
static async disconnect(): Promise<void> {
92+
static disconnect(): void {
10093
try {
101-
if (this.redis) {
102-
await this.redis.quit();
103-
this.redis = null;
104-
}
94+
this.getClient().close();
10595
} catch (error) {
10696
logger.error(error, "Error disconnecting from Redis:");
10797
}

src/libs/database/clickhouse/scripts/migrate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ async function main() {
126126
const migrator = new ClickHouseMigrator();
127127
const command = process.argv[2];
128128

129-
let executed: string[] = [];
130-
let all: Array<IMigrationFile> = [];
131-
let pending: Array<IMigrationFile> = [];
129+
let executed: string[];
130+
let all: Array<IMigrationFile>;
131+
let pending: Array<IMigrationFile>;
132132

133133
switch (command) {
134134
case "migrate":
Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,36 @@
1+
import { RedisClient as BunRedisClient } from "bun";
12
import { RedisConfig } from "@config";
2-
import Redis from "ioredis";
3+
4+
const buildUrl = (): string => {
5+
const auth = RedisConfig.REDIS_PASSWORD
6+
? `:${encodeURIComponent(RedisConfig.REDIS_PASSWORD)}@`
7+
: "";
8+
return `redis://${auth}${RedisConfig.REDIS_HOST}:${RedisConfig.REDIS_PORT}`;
9+
};
10+
11+
export interface QueueConnectionOptions {
12+
host: string;
13+
port: number;
14+
password?: string;
15+
maxRetriesPerRequest: null;
16+
}
317

418
export class RedisClient {
5-
private static redis: Redis | null = null;
6-
private static queueRedis: Redis | null = null;
19+
private static redis: BunRedisClient | null = null;
720

8-
static getRedisClient(): Redis {
21+
static getRedisClient(): BunRedisClient {
922
if (!this.redis) {
10-
this.redis = new Redis({
11-
host: RedisConfig.REDIS_HOST,
12-
port: RedisConfig.REDIS_PORT,
13-
password: RedisConfig.REDIS_PASSWORD || undefined,
14-
});
23+
this.redis = new BunRedisClient(buildUrl());
1524
}
16-
1725
return this.redis;
1826
}
1927

20-
static getQueueRedisClient(): Redis {
21-
if (!this.queueRedis) {
22-
this.queueRedis = new Redis({
23-
host: RedisConfig.REDIS_HOST,
24-
port: RedisConfig.REDIS_PORT,
25-
password: RedisConfig.REDIS_PASSWORD || undefined,
26-
maxRetriesPerRequest: null,
27-
});
28-
}
29-
30-
return this.queueRedis;
28+
static getQueueConnection(): QueueConnectionOptions {
29+
return {
30+
host: RedisConfig.REDIS_HOST,
31+
port: RedisConfig.REDIS_PORT,
32+
password: RedisConfig.REDIS_PASSWORD || undefined,
33+
maxRetriesPerRequest: null,
34+
};
3135
}
3236
}

src/modules/home/routes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ HomeRoutes.openapi(HealthRoute, async (c) => {
125125
// Redis remains unhealthy
126126
}
127127

128-
// Check Redis Queue
128+
// Check Redis Queue (shares the same Redis instance)
129129
try {
130130
const start = Date.now();
131-
await RedisClient.getQueueRedisClient().ping();
131+
await RedisClient.getRedisClient().ping();
132132
services.redisQueue = {
133133
status: "healthy",
134134
responseTime: Date.now() - start,

0 commit comments

Comments
 (0)