Dedupe and preload fetches

Wrap data functions in cache() and start them early.

From next-best-practices, data-patterns.md and metadata.md.

One query, two callers

Render twice, then tick the object argument.

Plain async functionBad
    const getPost = async (slug) => …
    Wrapped in cache()Good
      const getPost = cache(async (slug) => …)
      // lib/posts.ts
      import { cache } from 'react'
      
      export const getPost = cache(async (slug: string) => {
        return db.posts.findFirst({ where: { slug } })
      })
      
      // app/blog/[slug]/page.tsx
      export async function generateMetadata({ params }) {
        const { slug } = await params
        const post = await getPost(slug)
        return { title: post.title }
      }
      
      export default async function Page({ params }) {
        const { slug } = await params
        const post = await getPost(slug)
        return <article>{post.body}</article>
      }

      Preload before the other work

      Press Play, then drag both sliders.

      Await after the workBad
      work + query
      preloadUser(id) firstGood
      max(work, query)
      // lib/data.ts
      import { cache } from 'react'
      
      export const getUser = cache(async (id: string) => {
        return db.user.findUnique({ where: { id } })
      })
      
      export const preloadUser = (id: string) => {
        void getUser(id)
      }
      
      // app/user/[id]/page.tsx
      export default async function UserPage({ params }) {
        const { id } = await params
        preloadUser(id)
        await doOtherWork()
        const user = await getUser(id)
        return <div>{user.name}</div>
      }

      Each component fetches its own

      Toggle cache() and add components.

      Server component treeBad
        0 DB queries
        async function Avatar({ id }: { id: string }) {
          const user = await getUser(id)
          return <img src={user.avatarUrl} alt="" />
        }

        Why it matters, pros and cons

        Why it matters

        cache() gives every caller one promise per request; a preload starts it early.

        Pros

        • One query per argument per request.
        • Components fetch their own data, no drilling.
        • Latency overlaps work you already do.

        Cons

        • Scoped to one request; use 'use cache' across requests.
        • Object arguments are a new key every call.
        • GET fetch() is already memoized; wrap DB calls.