Skip to content

Proposal: Integrate AsyncLocalStorage for "Ambient" Context State #12

@EthanThatOneKid

Description

@EthanThatOneKid

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

  1. Cleaner Code: Removes the need to pass state or req through every layer of the service architecture.
  2. Better Integration: Allows 3rd-party logging or telemetry libraries to hook into the rt request lifecycle easily.
  3. Consistency: Aligns rt with modern patterns used in frameworks like Next.js (headers(), cookies()) or Hono.

Tasks

  • Create a best-practices/async-local-storage.ts example in the repository.
  • (Optional) Consider a built-in .useStorage(als) helper method on the Router class to automate the .run() wrapping.
  • Update documentation to show how to handle request-scoped logging using this pattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions