Problem Statement
Currently, rt allows for shared state via a generic State object passed through the router context:
const router = new Router<State>()
.get("/*", async ({ state, next }) => {
state.user = { name: "Wazoo" };
return await next();
})
.get("/:name", ({ state }) => {
// state is accessible here
});
While this works well for direct route handlers, it requires "prop drilling" the state or request object into every subsequent function call (e.g., database services, helper utilities, or deep-nested business logic) that needs access to request-specific data like the current user, trace IDs, or localized strings.
Proposed Solution
We should implement or provide a best-practice example for using Deno's node:async_hooks AsyncLocalStorage API within rt. This would allow developers to access the current request's state globally within the asynchronous execution flow without manually passing the state object.
Implementation Idea
We could introduce a middleware utility or a built-in method to wrap the router execution in an AsyncLocalStorage context.
Example Usage:
import { AsyncLocalStorage } from "node:async_hooks";
import { Router } from "@fartlabs/rt";
const als = new AsyncLocalStorage<State>();
// A utility function that can be called anywhere in the app
export function useCurrentUser() {
const store = als.getStore();
return store?.user;
}
const router = new Router<State>()
.use(async ({ state, next }) => {
// Wrap the entire downstream execution in the ALS context
return await als.run(state, () => next());
})
.get("/profile", () => {
const user = useCurrentUser(); // No need to pull from arguments!
return new Response(`Hello, ${user.name}`);
});
Reference
Benefits
- Cleaner Code: Removes the need to pass
state or req through every layer of the service architecture.
- Better Integration: Allows 3rd-party logging or telemetry libraries to hook into the
rt request lifecycle easily.
- Consistency: Aligns
rt with modern patterns used in frameworks like Next.js (headers(), cookies()) or Hono.
Tasks
Problem Statement
Currently,
rtallows for shared state via a genericStateobject passed through the router context:While this works well for direct route handlers, it requires "prop drilling" the
stateorrequestobject into every subsequent function call (e.g., database services, helper utilities, or deep-nested business logic) that needs access to request-specific data like the current user, trace IDs, or localized strings.Proposed Solution
We should implement or provide a best-practice example for using Deno's
node:async_hooksAsyncLocalStorageAPI withinrt. This would allow developers to access the current request's state globally within the asynchronous execution flow without manually passing thestateobject.Implementation Idea
We could introduce a middleware utility or a built-in method to wrap the router execution in an
AsyncLocalStoragecontext.Example Usage:
Reference
Benefits
stateorreqthrough every layer of the service architecture.rtrequest lifecycle easily.rtwith modern patterns used in frameworks like Next.js (headers(),cookies()) or Hono.Tasks
best-practices/async-local-storage.tsexample in the repository..useStorage(als)helper method on theRouterclass to automate the.run()wrapping.