Keep request data out of shared module state
Do not store request- or user-specific data in mutable module-level variables on the server.
Pass it through props, function arguments, or a request-scoped API such as React.cache.
Implementation
- Treat server module scope as memory shared by every request the process handles.
- Pass request data, such as the current user, down the component tree as props, or read it through a request-scoped function.
- Module-level values are fine when they are immutable and identical for every request, such as static configuration or assets loaded once.
- A shared cache is fine when it is designed for cross-request reuse and its keys include everything that makes an entry request-specific.
Rationale
A server can render several requests concurrently in one process, interleaving their asynchronous work. If one render writes a module-level variable and another render overwrites it before the first reads it, the first request renders the second request's data. The result is a race condition that can expose one user's data to another.
Examples
Incorrect (counterexample):
let currentUser: User | null = null;
export default async function Page() {
currentUser = await auth();
return <Dashboard />;
}
async function Dashboard() {
return <div>{currentUser?.name}</div>;
}
If two requests overlap, request B can overwrite currentUser before request A renders Dashboard, so user A sees user B's name.
Correct:
export default async function Page() {
const user = await auth();
return <Dashboard user={user} />;
}
function Dashboard({ user }: { user: User | null }) {
return <div>{user?.name}</div>;
}
Validation
Search server modules for top-level let declarations and for module-level objects or collections that are mutated inside request handlers or components.
Each one should hold only request-independent data or be a cache keyed by all request-specific inputs.
Immutable configuration, assets loaded once, and correctly keyed caches are not violations.