admin管理员组

文章数量:1398814

I need to access data that is specific to a request in a few different places (all server-side) in a next.js app -- for example, the Host header or a request ID. For now, my team has been using the headers function and other request-specific state management functions from next.js.

This has worked OK so far. However, with next.js 15 those functions (headers, cookies, etc) have become async, and I have a few non-async functions that currently call them and must remain non-async (because they're passed as arguments to synchronous third-party APIs).

Eg.:


// combine expects a sync callback
combine(getCustomerSlug);

// with nextjs 15, this function will need to be async
getCustomerSlug() 
{
   let host = headers();
   //...
}

This could easily be solved by first calling eg. headers on an async context (eg. middleware), then storing the result somewhere globally accessible for the lifetime of the request, but I'm failing to find a way to do that on next.js; In fact, this is actually what we wanted to do in the first place before we gave up and decided to just use those functions directly elsewhere.

One solution I've tried is using Node.js' AsyncLocalStorage: start a context when the middleware starts running, and end it when the middleware ends... except since next.js 12 middleware doesn't look much like middleware anymore, and AFAIK there's no way you can wait for your route's response. On this same vein, I also though about decorating every single route with a call to AsyncLocalStorage.run but that's clearly not ideal.

I've also thought about simply using global variables, but I guess that wouldn't work unless next.js searializes and runs each request synchronously, which I'm guessing it doesn't, or if it spawns a new process for every request.

A third similar option would be to have a global Map<RequestId, State>, but as there doesn't seem to be a way to define code in a single place to safely and reliably execute logic right before a response is sent in next.js (ahem middleware), that map could grow indefinitely as the server receives more requests.

I need to access data that is specific to a request in a few different places (all server-side) in a next.js app -- for example, the Host header or a request ID. For now, my team has been using the headers function and other request-specific state management functions from next.js.

This has worked OK so far. However, with next.js 15 those functions (headers, cookies, etc) have become async, and I have a few non-async functions that currently call them and must remain non-async (because they're passed as arguments to synchronous third-party APIs).

Eg.:


// combine expects a sync callback
combine(getCustomerSlug);

// with nextjs 15, this function will need to be async
getCustomerSlug() 
{
   let host = headers();
   //...
}

This could easily be solved by first calling eg. headers on an async context (eg. middleware), then storing the result somewhere globally accessible for the lifetime of the request, but I'm failing to find a way to do that on next.js; In fact, this is actually what we wanted to do in the first place before we gave up and decided to just use those functions directly elsewhere.

One solution I've tried is using Node.js' AsyncLocalStorage: start a context when the middleware starts running, and end it when the middleware ends... except since next.js 12 middleware doesn't look much like middleware anymore, and AFAIK there's no way you can wait for your route's response. On this same vein, I also though about decorating every single route with a call to AsyncLocalStorage.run but that's clearly not ideal.

I've also thought about simply using global variables, but I guess that wouldn't work unless next.js searializes and runs each request synchronously, which I'm guessing it doesn't, or if it spawns a new process for every request.

A third similar option would be to have a global Map<RequestId, State>, but as there doesn't seem to be a way to define code in a single place to safely and reliably execute logic right before a response is sent in next.js (ahem middleware), that map could grow indefinitely as the server receives more requests.

Share Improve this question edited Apr 2 at 22:19 andre_ss6 asked Mar 26 at 22:45 andre_ss6andre_ss6 1,1451 gold badge16 silver badges38 bronze badges 3
  • Are you trying to reduce the number of repeated calls to headers(), or are you just unable to convert synchronous code to asynchronous code? If it's the latter, please share your code. – Duannx Commented Apr 1 at 6:35
  • @Duannx Both. Naturally, we want to have control over how we manage state instead of having to rely on storing state as headers or cookies (or framework functions, for that matter). As for "converting sync to async", the issue is that we have third-party APIs which expect a synchronous callback. I'll update with an example. – andre_ss6 Commented Apr 2 at 22:20
  • For your first problem, Next.js already caches the header() call for you. This means the next call to header() will get the data directly from the cache. It’s similar to manually implementing a separate shared state. However, if you want more customization in sharing state, you can use React's cache API. It is the recommended way to share state between components in SSR. – Duannx Commented Apr 3 at 11:05
Add a comment  | 

1 Answer 1

Reset to default -1

Storing state on the server is an antipattern. AsyncLocalStorage has its uses but can bloat your server pretty quick. Use after if you're doing post processing. Using a local global variable, ie a singleton, won't work either since nextjs runs multiple context at once.

本文标签: reactjsHow to share requestwide serverside state in nextjsStack Overflow