Skip to content

Commit 9c9de7f

Browse files
brophdawg11joseph0926
authored andcommitted
Add docs on middleware and AsyncLocalStorage (#14146)
1 parent 371337f commit 9c9de7f

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

docs/how-to/middleware.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,57 @@ const user = context.get(userContext); // Returns User type
350350
// context.user = user; // Could be anything
351351
```
352352

353+
#### Context and AsyncLocalStorage
354+
355+
Node provides an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) API which gives you a way to provide values through asynchronous execution contexts. While this is a Node API, most modern runtimes have have made it (mostly) available (i.e., [Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/nodejs/asynclocalstorage/), [Bun](https://bun.sh/blog/bun-v0.7.0#asynclocalstorage-support), [Deno](https://docs.deno.com/api/node/async_hooks/~/AsyncLocalStorage)).
356+
357+
In theory, we could have leveraged `AsyncLocalStorage` directly as the way to pass values from middlewares to to child routes, but the lack of 100% cross-platform compatibility was concerning enough that we wanted to still ship a first-class `context` API so there would be way to publish reusable middleware packages guaranteed to work in a runtime-agnostic manner.
358+
359+
That said, this API still works great with React Router middleware and can be used in place of, or alongside of the `context` API:
360+
361+
<docs-info>`AsyncLocalStorage` is _especially_ powerful when using [React Server Components](../how-to/react-server-components) because it allows you to provide information from `middleware` to your Server Components and Server Actions because they run in the same server execution context 🤯</docs-info>
362+
363+
```tsx filename=app/user-context.ts
364+
import { AsyncLocalStorage } from "node:async_hooks";
365+
366+
const USER = new AsyncLocalStorage<User>();
367+
368+
export async function provideUser(
369+
request: Request,
370+
cb: () => Promise<Response>,
371+
) {
372+
let user = await getUser(request);
373+
return USER.run(user, cb);
374+
}
375+
376+
export function getUser() {
377+
return USER.getStore();
378+
}
379+
```
380+
381+
```tsx filename=app/root.tsx
382+
import { provideUser } from "./user-context";
383+
384+
export const unstable_middleware: Route.unstable_MiddlewareFunction[] =
385+
[
386+
async ({ request, context }, next) => {
387+
return provideUser(request, async () => {
388+
let res = await next();
389+
return res;
390+
});
391+
},
392+
];
393+
```
394+
395+
```tsx filename=app/routes/_index.tsx
396+
import { getUser } from "../user-context";
397+
398+
export function loader() {
399+
let user = getUser();
400+
//...
401+
}
402+
```
403+
353404
### The `next` Function
354405

355406
The `next` function logic depends on which route middleware it's being called from:

0 commit comments

Comments
 (0)