mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[GH-ISSUE #623] RFC: Per-request store API #132
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#132
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @james-elicx on GitHub (Mar 21, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/623
This issue is a request for feedback on the public API for our per-request store API. The idea was originally proposed in #608.
Internally, the store would interact with a map that's inside a request-scoped
AsyncLocalStorage.I've been doing some thinking this morning about the public API that we'd be exposing, as it would be nice have it well-defined from the get-go to avoid potentially introducing breaking changes in the future.
Store Interface
My thinking is that we would want to expose full control for interacting with the underlying store, which also providing some higher-level abstractions to reduce boilerplate.
The store itself would expose the following:
Methods accepting a callback may possibly need async variants as well, or the other way around with async as the default and sync as the variant, or neither if we find a nice way to make both work in one function.
Store Consumption
I had been thinking through a few different ways to approach this, and the following is what I arrived at for a nice balance of type-safety and control.
It would be great to get thoughts from others, or suggestions if you would like to see something else!
Function that returns a type-safe interface to the underlying map
The alternative approach that was on my mind would be a Proxy to the underlying map where you pass the type stored to each function call, but that would sacrifice some of the type-safety.
Abstraction on top of the store for callbacks
Another option that would be interesting to explore is a
cacheForRequestfunction, that takes a callback and lazily initialises the value in the request store on first access. Similar to how React'scachefunction works, but not limited to server components.@JamesbbBriz commented on GitHub (Mar 22, 2026):
One thing worth flagging with
createRequestStore— since the underlying Map is shared per-request, two unrelated modules using the same key will collide silently:cacheForRequestdoesn't have this problem since the function reference itself acts as the key.Also — for the async case, I think caching the Promise (not the resolved value) is important. Otherwise two concurrent calls during the first await both trigger the factory:
@james-elicx commented on GitHub (Mar 22, 2026):
In theory you would only ever need to create the store once, and you can then re-use it everywhere with type-safety, since it would essentially just be a type utility.
Good shout on the promises
@JamesbbBriz commented on GitHub (Mar 28, 2026):
Implemented the
cacheForRequest()API in #646 — it covers the higher-level factory cache use case from this RFC. Uses function identity as the cache key with aWeakMapto avoid memory leaks, and includes rejected-Promise self-healing. Would love feedback on whether this aligns with the direction you had in mind.@james-elicx commented on GitHub (Mar 28, 2026):
I'll close this as completed with the
cacheForRequest()utility for now - can re-visit if a lower-level API is needed.