next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Docs: How to "use cache" but only at runtime (not build)

Open gaearon opened this issue 1 month ago • 6 comments

What is the documentation issue?

I want to "use cache" some DB queries (in memory), but I absolutely don't want them to be pre-rendered during the build (my build environment doesn't even have access to the database). How do I exclude those from prerendering?

I initially thought I could await connection() but it doesn't work inside "use cache".

Is there any context that might help us understand?

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

https://nextjs.org/docs/app/getting-started/cache-components

gaearon avatar Dec 01 '25 11:12 gaearon

AFAIK, use cache is for static (build-time) caching. If you want runtime caching, you are supposed to use use cache: remote. The docs explicitly say that use cache stops working in dynamic (connection() etc) contexts: https://nextjs.org/docs/app/api-reference/directives/use-cache-remote#dynamic-context-detection

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

Example from docs:

import { connection } from 'next/server'
import { cacheLife, cacheTag } from 'next/cache'
 
export default async function DashboardPage() {
  // Make context dynamic
  await connection()
 
  const stats = await getGlobalStats()
 
  return <StatsDisplay stats={stats} />
}
 
async function getGlobalStats() {
  'use cache: remote'
  cacheTag('global-stats')
  cacheLife({ expire: 60 }) // 1 minute
 
  // This expensive database query is cached and shared across all users,
  // reducing load on your database
  const stats = await db.analytics.aggregate({
    total_users: 'count',
    active_sessions: 'count',
    revenue: 'sum',
  })
 
  return stats
}

arunanshub avatar Dec 01 '25 12:12 arunanshub

You'd need to defer the function call to runtime.

import { connection } from 'next/server'
import { Suspense } from 'react'
 
export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <ProfileContent />
    </Suspense>
  )
}
 
// Component (not cached) reads runtime data
async function ProfileContent() {
  await connection(); // prerendering stops here
  const content = await getProfileContent(); // this happens at runtime rather
  return /* UI */
}
 
// Cached component/function receives data as props
async function getProfileContent() {
  'use cache'
  // query and return db data
}

Thanks for the feedback. I'll do some more updates soon, I have a few things I need to rectify.


@arunanshub from what I've been discussing with the team, what that docs page is trying to do is, narrow the usage of use cache: remote to the so called, dynamic context, the operations that do not contribute to the static shell, as a way to deter people from using it in other situations.

I have a PR to change how we teach it here, https://github.com/vercel/next.js/pull/86546 - still waiting on feedback though.

icyJoseph avatar Dec 01 '25 13:12 icyJoseph

It's kind of inconvenient to add await connection() to every callsite... What I'm trying to express is:

  • Some specific DB calls (which are same for all users and don't "really" depend on connection) should be cached
  • But my build environment just doesn't have DB access so I can't physically do the prerendering

Maybe the fix is just to fix the pipeline but idk.

gaearon avatar Dec 01 '25 14:12 gaearon

@icyJoseph Thanks for the clarification. The difference between use cache and use cache: remote wasn't apparent from the docs itself. I assumed in both cases nextjs uses the data cache. I'm wondering:

  1. How is the usage of cache directive affected in self hosted contexts?
  2. How is it affected in dynamic routes, eg. /users/[id]?

arunanshub avatar Dec 02 '25 11:12 arunanshub

@arunanshub I found this explanation pretty helpful: https://github.com/vercel/next.js/issues/85240#issuecomment-3560124078

gaearon avatar Dec 02 '25 14:12 gaearon

Didn't mean to leave ya hanging here, I just didn't have any good option, I forwarded this issue to the team.

I kept going back to: if your build pipeline did include the DB connection, the data would be included in the static shell, and at runtime you wouldn't need to show fallback UI.

Any other refactor that I am aware of, would miss on that benefit.

icyJoseph avatar Dec 02 '25 18:12 icyJoseph

Hi @gaearon and team, I’d like to contribute toward addressing this documentation gap. After reviewing the discussion and the current documentation, I believe there’s an opportunity to clarify a key challenge: using cache components in environments where build-time database access is not available. Proposed Documentation Structure

  1. Runtime-Only Caching Patterns Clear examples demonstrating cache usage without build-time prerendering Environment-specific configurations for CI/CD pipelines that lack database access
  2. Build vs. Runtime Caching Decision Guide Guidance on when to use "use cache" versus "use cache: remote" Performance implications and trade-offs Recommended practices for different deployment scenarios
  3. Practical Implementation Patterns Alternative approaches to avoid repetitive await connection() calls Component composition patterns for improved code organization Error-handling strategies for environments without database connectivity
  4. Real-World Examples End-to-end code samples for common use cases Examples integrating popular databases and ORMs Production deployment considerations Proposed Plan Draft the documentation sections with concrete code examples Review the content with maintainers to ensure technical accuracy Integrate the new material into the existing cache components documentation Add cross-references to related APIs where relevant This addition would complement the existing cache documentation while directly addressing the build-environment constraints discussed here. If this approach sounds reasonable, I’m happy to proceed and collaborate with @icyJoseph and the team on the technical details. Thanks!

AbhishekGiri04 avatar Dec 15 '25 16:12 AbhishekGiri04

if your build pipeline did include the DB connection, the data would be included in the static shell, and at runtime you wouldn't need to show fallback UI.

What’s the canonical solution when the data is time-sensitive? Say I deploy once a week but there’s new content every few hours. I wouldn’t want to show stale content from a few days ago ever.

gaearon avatar Dec 19 '25 09:12 gaearon

It’s also a bizarre solution because the content is user-generated and could be made private later or violate guidelines or whatever. I wouldn’t want to embed it in my build output and keep serving it. Caching for say five minutes is acceptable but not any longer. In that case, doing it during the build seems like unnecessary complication for a case I don’t actually want to handle.

gaearon avatar Dec 19 '25 09:12 gaearon