Request Context Store (request-context.js)
Examine the server thread context isolation manager, AsyncLocalStorage bindings, and client-side safe mocks.
Key File Location: ./dinou/core/request-context.js💡 Overview
Unlike Client Components (which query the active tab url or browser storage), Server Components compile on-demand for incoming requests. A component deep inside the React tree might need to query cookie tokens or headers without passing parameters through props from top-level layouts.
The request-context.js file provides this functionality. It leverages Node's AsyncLocalStorage to store request-scoped data (like headers, search params, and cookies), exposing a clean getContext() accessor.
📊 Context Scope Flow
The flowchart below shows how request contexts are isolated and retrieved:
⚡ AsyncLocalStorage & Isolation
In standard Node.js scripts, global variables are shared across all requests. Under high traffic, storing request data in globals would cause **cross-talk bugs** (e.g. User A receives User B's private account data).
Dinou prevents this via AsyncLocalStorage:
- Thread Isolation: Associates request state maps (
{ req, res }) with asynchronous execution chains. When a Server Component executes an async operation, Node carries the context along automatically. - Global Persistency: Binds storage instances using
Symbol.for()on the global scope. This preserves request contexts during Hot Module Replacement (HMR) reloads. - Safe Client Bypasses: Mocks execution APIs in browser threads, rendering warnings to developers if
getContext()is called in Client Components.
⚙️ Complete Code Walkthrough
Below is the full, complete code of request-context.js:
// dinou/core/request-context.js
const DINOU_CONTEXT_KEY = Symbol.for("dinou.request.context.storage");
let requestStorage;
// 1. Initialize AsyncLocalStorage only on Server side
if (typeof window === "undefined") {
const nodeRequire =
typeof module !== "undefined" && typeof module.require === "function"
? module.require.bind(module)
: null;
if (nodeRequire) {
const { AsyncLocalStorage } = nodeRequire("node:async_hooks");
// Persist storage globally to prevent hot-reload wipes
if (!global[DINOU_CONTEXT_KEY]) {
global[DINOU_CONTEXT_KEY] = new AsyncLocalStorage();
}
requestStorage = global[DINOU_CONTEXT_KEY];
}
} else {
// 2. Client Side Fallback Mock: prevents errors during build/hydration passes
requestStorage = {
run: (store, callback) => callback(),
getStore: () => undefined,
};
}
function getContext() {
if (typeof window !== "undefined") {
console.error(
"[Dinou] ❌ You are calling getContext() inside a Client Component running in the browser. " +
"This function is Server-Only. Pass the data as props from a Server Component instead."
);
return {};
}
if (!requestStorage) return undefined;
// 3. Retrieve the context map associated with the active execution thread
const store = requestStorage.getStore();
return store;
}
module.exports = {
requestStorage,
getContext,
};