Questions
28 of 33
1Explain server-side rendering (SSR) in Next.js.
2List the benefits of Server-Side Rendering in Next.js.
3What are server components?
4How to use server components in next js?
5What is the React Server Component Payload (RSC)?
6Describe how server components are rendered.
7Describe server rendering strategies
8How can you keep Server-only Code out of the Client Environment
9What are the issues of using context provider at the root of the application?
10List dynamic functions available to server components.
11List all Dynamic APIs.
12How do we opt for Static Rendering, Dynamic Rendering and Streaming?
13Explain the difference between SSR, SSG, ISR, and CSR in Next.js. When would you choose each?
14How does getServerSideProps work internally? What is its execution context?
15What are the performance trade-offs of SSR vs SSG in a high-traffic production app?
16How does Next.js handle hydration? What is a hydration mismatch, and how do you debug it?
17What happens when getServerSideProps throws an error? How do you handle it gracefully?
18How would you architect a Next.js app that needs both personalised (SSR) and cacheable (SSG) pages at scale?
19Explain the App Router vs Pages Router SSR model differences. How does React Server Components change the SSR paradigm?
20How do React Server Components (RSC) differ from traditional SSR? Can they be used together?
21How does streaming SSR work in Next.js 13+? What role does Suspense play?
22How would you implement partial hydration or Islands Architecture in Next.js?
23Explain how Next.js handles data fetching waterfall problems in SSR and how you'd mitigate them.
24How would you implement partial hydration or Islands Architecture in Next.js?
25How do you implement caching strategies for SSR responses in Next.js? (CDN, Cache-Control, stale-while-revalidate)
26How would you reduce TTFB (Time to First Byte) in an SSR-heavy Next.js app?
27What's the difference between fetch caching in the App Router vs traditional getServerSideProps?
28How do you avoid redundant database/API calls across multiple server components on the same page?
29How do you securely handle authentication in SSR? What are the risks of passing tokens via cookies vs headers?
30How do you prevent sensitive server-side data from leaking to the client bundle?
31How would you implement role-based rendering on the server without exposing protected routes to the client?
32How do you debug SSR-only issues that don't appear in local development?
33How can we control dynamic rendering behaviour in next js?
28 / 33

How do you avoid redundant database/API calls across multiple server components on the same page?

Avoid redundant calls using React's cache() for non-fetch operations, automatic fetch deduplication for API calls, and the experimental 'use cache' directive for advanced scenarios with persistent caching

In Next.js App Router, redundant data fetching across multiple Server Components on the same page is a common concern, especially when components like layouts, pages, and generateMetadata all need the same data [citation:1][citation:10]. Next.js provides multiple layers of deduplication: automatic fetch deduplication for HTTP requests, React's cache() function for any async operation, and the experimental 'use cache' directive for persistent caching. The key insight is that data fetching in Server Components is automatically deduplicated within a single render pass, but only when using the right tools for each scenario [citation:4][citation:9].

Understanding the Problem
  1. 1

    Multiple components (layout, page, generateMetadata) often need the same data like user profiles or blog posts [citation:1].

  2. 2

    Without deduplication, each component makes separate API/database calls, increasing server load and response time [citation:2].

  3. 3

    This is especially problematic with ORMs or database clients that don't automatically deduplicate like fetch() does [citation:4].

  4. 4

    The issue affects both performance and cost, particularly with paid API services or rate-limited endpoints [citation:10].

When using the native fetch() API within Server Components, Next.js automatically deduplicates requests. React patches globalThis.fetch on the server side, so that multiple calls to the same URL with the same options within a single render pass will result in only one actual network request [citation:4]. This works because fetch responses are memoized per request, stored in the React cache for the duration of the render pass [citation:8]. This is the simplest solution and requires no additional code—just use fetch consistently across components [citation:1].

Automatic fetch Deduplication Example

For data fetching that doesn't use fetch—such as database queries with ORMs (Prisma, Drizzle), SDKs, or custom API clients—you need to manually opt into deduplication using React's cache() function [citation:4]. cache() creates a memoized version of your async function that returns the same promise for identical arguments within a single render pass [citation:10]. This is perfect for database calls or when you need to deduplicate expensive computations across components like layouts, pages, and generateMetadata [citation:2][citation:9]. The cache is per-request only and doesn't persist across different user requests [citation:4].

React cache() with Database Queries
Important: Object Arguments and cache()
  1. 1

    React's cache() uses shallow equality checks on arguments. If you pass objects, two calls with identically structured but different object references won't be deduped [citation:10].

  2. 2

    ❌ getProduct({ id: '123' }) and getProduct({ id: '123' }) → two separate calls (different object references)

  3. 3

    ✅ getProduct('123') and getProduct('123') → single call (primitive string equality)

  4. 4

    ✅ getProduct(id) with the same variable → single call

  5. 5

    Solution: Pass primitives (strings, numbers) rather than objects as arguments, or ensure object references are identical [citation:10].

You can combine both approaches by wrapping fetch-based operations in cache() when you need additional control or when working with fetch variants that might not be automatically deduped. Some HTTP clients like Ky don't automatically participate in React's fetch deduplication, requiring explicit cache() wrapping [citation:4]. This pattern also allows you to add logging, transformation, or error handling while still benefiting from deduplication.

Combining fetch with cache()

While React's cache() only deduplicates within a single request, Next.js provides unstable_cache (from 'next/cache') for persisting results across multiple requests and even deployments [citation:4]. This is ideal for expensive computations or data that rarely changes. Unlike React.cache, unstable_cache writes to the filesystem or persistent data cache, meaning 100 page visits might only trigger one database query [citation:4]. Use this for truly static or slowly changing data, but be cautious with user-specific data as it's shared across users.

unstable_cache for Cross-Request Caching

Next.js is introducing a new experimental 'use cache' directive that simplifies caching dramatically [citation:5]. You can add 'use cache' to any async function or component, and Next.js automatically handles deduplication, persistent caching, and invalidation. This unifies the mental model: instead of choosing between fetch, cache(), and unstable_cache, you simply mark what should be cached and let the framework decide how [citation:5][citation:3]. Combined with cacheTag() and cacheLife(), you get fine-grained control over cache behavior. This is experimental and requires the dynamicIO flag, but represents the future of caching in Next.js [citation:5].

Experimental 'use cache' Directive
Choosing the Right Deduplication Strategy
  1. 1

    Use automatic fetch deduplication when: You're using native fetch() for API calls and don't need custom logic [citation:4][citation:8].

  2. 2

    Use React.cache() when: You're using database clients (Prisma, Drizzle), SDKs, or any async function that isn't fetch [citation:4][citation:9].

  3. 3

    Use React.cache() also when: You need to dedupe across generateMetadata and page components with custom data fetching [citation:1][citation:10].

  4. 4

    Use unstable_cache when: You need to cache across different user requests for expensive operations (site stats, global config) [citation:4].

  5. 5

    Consider 'use cache' (experimental) for: New projects where you want the simplest mental model and can tolerate experimental APIs [citation:5].

A frequent issue is deduplication not working with generateMetadata and page components [citation:10]. This often happens because: 1) Using an HTTP client like Axios or Ky that doesn't use fetch [citation:4], 2) Passing objects as arguments to cached functions [citation:10], or 3) The functions executing in parallel before cache can establish the promise. Solution: Ensure you're using fetch or wrap all data access in cache(), and pass primitive arguments. Also note that cache() only works on the server—client components need different strategies [citation:4].