vercel / vercel/next.js

Docs: "use cache" and "use server" should document how variables defined or imported in the module scope are handled

Open
#74,498 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

What is the documentation issue?

In the documentation of "use cache", it is stated that:

"Any non-serializable arguments, props, or closed-over values will turn into opaque references inside the cached function, and can be only passed through and not inspected nor modified. These non-serializable values will be filled in at the request time and won't become a part of the cache key."

In the blog post explaining use cache in more details (Composable caching), this example is given:

function Profile({ id }) {
  async function getNotifications(index, limit) {
    'use cache';
    return await db
      .select()
      .from(notifications)
      .limit(limit)
      .offset(index)
      .where(eq(notifications.userId, id));
  }
 
  return <User notifications={getNotifications} />;
}

Now the db variable in the example is a closed-over non-serializable value, but in the example it is introspected without issue. (its select property is read).

After inspection and sandboxing, I figured values imported or defined in the module scope of the file of the cached function bypass the serializability checks because the compiler hoists the compiled cached functions to the module scope, making all values in the module scope directly available to them. The same applies to "use server" functions, where values in the module scope bypass being part of the closure snapshot. I guess this is a feature, not a bug, as it allows to use non-serializable values to define the function's behavior, like a db connection. But the behavior is not documented anywhere, not even in React's "use server" docs.

This thus feels like compiler magic, and it makes Next less architecture agnostic. For example, I am using dependency injection to pass my db connection around my code, but that makes it impossible to pass it to "use cache" or "use server" functions. There are easy ways around it, and I don't think my use case is general enough to change how "use cache" or "use server" is implemented, but I feel it should at least be documented somewhere, as it might affect someone's architectural decisions.

I had opened a PR recently adding it to the docs, but now I don't even know if it should belong in React's or Next's docs, so I closed the PR.

Is there any context that might help us understand?

Blog posts:
Composable caching
Our journey with caching

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/api-reference/directives/use-cache

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the use-cache documentation page and compare its serializability wording with the Composable caching and Our journey with caching blog posts. Confirm how module-scope and imported values differ from closed-over values, decide whether the explanation belongs in Next.js or React documentation, and update the relevant documentation so the behavior and architectural implications are clear.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nextjs, react
Domain
documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.