Context Propagation Pattern
Understand the core pattern for propagating custom Express.js request context (sessions, locales, tenant databases) down into React Server Components and Server Functions.
In Dinou, there are no black boxes. When you eject the framework, the Express server is completely yours. Propagating custom request metadata down to your React application is simple, structured, and consistent.
💡 Philosophy
In standard full-stack applications, you frequently need to access request-specific information within your UI components (like the authenticated user session, active language locales, custom subdomains, or tenant databases).
Dinou provides an elegant getContext() API backed by Node's AsyncLocalStorage. By populating the request context at the Express level, you make these values instantly accessible anywhere in your server-side React code with zero prop-drilling.
getProps), and Server Functions (mutations) using the native getContext() API.🎯 The Three Crucial Points
To propagate any request variable (such as req.locale for internationalization or req.userId for Clerk Authentication) from Express to React, you must map it in exactly three specific locations inside your ejected dinou/core/server.js file:
1. Standard Page Context: getContext()
This function builds the request context used during initial HTML server-side rendering (SSR) and client-side RSC data fetches. Add your custom properties directly to the return object:
// dinou/core/server.js
function getContext(req, res) {
return {
req: {
cookies: { ...req.cookies },
headers: { ... },
query: { ...req.query },
path: req.path,
method: req.method,
// 🌐 1. Propagate custom properties here:
locale: req.locale,
userId: req.userId,
},
res: { ... }
};
}2. Server Function Context: getContextForServerFunctionEndpoint()
This function builds the context for React Server Functions (Server Actions) executed inside forms or interactive client-side events. Ensure the same variables are propagated here to protect and authorize database mutations:
// dinou/core/server.js
function getContextForServerFunctionEndpoint(req, res) {
return {
req: {
cookies: { ...req.cookies },
headers: { ... },
query: { ...req.query },
path: req.path,
method: req.method,
// 🌐 2. Propagate custom properties here:
locale: req.locale,
userId: req.userId,
},
res: { ... }
};
}3. HTML Compiler Subprocess Context: contextForChild
During initial page loads, Dinou spawns a background child process to pre-render the dynamic HTML stream. To prevent React hydration mismatches on the client, the child process must receive the exact same request context as the parent process.
Locate the Express wildcard GET handler (app.get(/^\/.*\/?$/)) and serialize your variables inside the contextForChild.req object:
// In server.js wildcard route handler: app.get(/^/.*/?$/)
const contextForChild = {
req: {
query: { ...req.query },
cookies: { ...req.cookies },
headers: { ... },
path: req.path,
method: req.method,
// 🌐 3. Propagate custom properties here:
locale: req.locale,
userId: req.userId,
},
};⚡ Consuming Context in React
Once registered in the three points, you can consume the variables cleanly in any server-side React code:
In Page Functions (page_functions.ts)
Enable request-time dynamic evaluation by returning dynamic() { return true; }, then fetch variables to load specific user details or localized content:
import { getContext } from "dinou";
export function dynamic() {
return true; // Evaluate route dynamically at request-time
}
export async function getProps() {
const context = getContext();
// Safely consume context variables
const locale = context?.req?.locale || "en";
const userId = context?.req?.userId;
return {
page: {
locale,
userId
}
};
}In Server Functions (Actions)
Verify identity and authorize mutations directly within actions:
"use server";
import { getContext } from "dinou";
export async function saveProfileData(data: any) {
const context = getContext();
const userId = context?.req?.userId;
if (!userId) {
throw new Error("Unauthorized: Invalid session token.");
}
// Update record in database
await prisma.user.update({
where: { clerkId: userId },
data
});
}