How to best handle long-running servers that reuse connections across requests? #3499
-
|
Hey, I am migrating a hono service from cf workers to a node service and need to replace something that was a simple http request before with multiple Redis connections. I am wondering what's the cleanest and most idiomatic way is. My current approach is simply declaring a variable outside of the handler and on first request initialise it. Its then passed to the context as usual. let fnClient: FunctionsClient;
const app = new Hono<Env>();
app.use((c, next) => {
if (!fnClient) {
fnClient = new FunctionsClient({
host: env("REDIS_HOST"),
port: env("REDIS_PORT"),
});
}
c.set("services", {
logger,
fnClient,
});
return next();
});This feels a big sluggish for me. Is there a better way? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
|
I guess you're looking for something like this. hono.js middleware can also be dependency injection! import { Hono } from "hono"
import { createMiddleware } from "hono/factory"
import { Redis } from "ioredis"
const redisUrls = {
default: "redis://127.0.0.1:6379",
test1: "redis://127.0.0.1:16379",
test2: "redis://127.0.0.1:26380",
}
type RedisKey = keyof typeof redisUrls
const clients = new Map<RedisKey, Redis>()
const useRedis = <ContextKey extends string = "redis">(
redisKey: RedisKey,
contextKey?: ContextKey,
) => {
type Env = {
Variables: {
[key in ContextKey]: Redis
}
}
contextKey ??= "redis" as ContextKey
return createMiddleware<Env>(async (c, next) => {
if (!clients.has(redisKey)) {
const redisUrl = redisUrls[redisKey]
clients.set(redisKey, new Redis(redisUrl))
}
const redis = clients.get(redisKey)!
c.set(contextKey, redis)
await next()
})
}
const app = new Hono()
.get("/", useRedis("default"), async (c) => {
const redis = c.get("redis") // redis://127.0.0.1:6379
return c.text("")
})
.get("/foo", useRedis("test1"), async (c) => {
const redis = c.get("redis") // redis://127.0.0.1:16379
return c.text("")
})
.get(
"/multi",
useRedis("test1", "redis1"),
useRedis("test2", "redis2"),
async (c) => {
const redis1 = c.get("redis1") // redis://127.0.0.1:16380
const redis2 = c.get("redis2") // redis://127.0.0.1:26380
return c.text("")
},
) |
Beta Was this translation helpful? Give feedback.
-
|
Totally +1 to the “middleware as DI / client cache” pattern shown above — that’s basically the right primitive for long-running Node/Bun servers. If you find yourself doing this for more than one thing (Redis, DB, queues, SDK clients, etc.), one approach that’s worked well for us is to wrap that idea into a tiny DI layer that still feels “Hono-native”: explicit factories, no decorators/reflect-metadata, and middleware creating a request scope. We OSS’d what we use in production here: @circulo-ai/di. It’s deliberately small, but adds a couple things that become handy once the app grows past a single cached client:
Here’s the same pattern from this thread, but generalized: import { Hono } from "hono";
import {
ServiceCollection,
createContainerMiddleware,
resolveFromContext,
} from "@circulo-ai/di";
import { Redis } from "ioredis";
const services = new ServiceCollection();
// long-lived clients
services.addSingleton("Redis", () => new Redis(process.env.REDIS_URL!), {
key: "default",
});
services.addSingleton("Redis", () => new Redis(process.env.REDIS_ANALYTICS!), {
key: "analytics",
});
// per-request stuff
services.addScoped("RequestId", () => crypto.randomUUID());
const provider = services.build();
const app = new Hono();
app.use("*", createContainerMiddleware(provider));
app.get("/stats", (c) => {
const redis = resolveFromContext(c, "Redis", "analytics");
const reqId = resolveFromContext(c, "RequestId");
// ...
return c.json({ ok: true, reqId });
});So you still get the “cache once, reuse forever” behavior this discussion is targeting, but with a consistent way to scale it across many services and keep request lifecycles tidy. Sharing in case it’s useful for anyone hitting the “one cached client turned into ten” stage — happy to answer questions. |
Beta Was this translation helpful? Give feedback.

I guess you're looking for something like this.
hono.js middleware can also be dependency injection!