← Back to DevBytes

Server-Side Rendering with Apollo: SSR, SSG, ISR

Introduction to Server-Side Rendering with Apollo

Building modern web applications often requires fetching data from a GraphQL API. While client-side rendering is the default for many single-page applications, it can lead to slower initial page loads and poor SEO. By combining Apollo Client with server-side rendering strategies, you can pre-render your pages on the server, sending fully formed HTML to the browser. This tutorial explores how to implement Server-Side Rendering (SSR), Static Site Generation (SSG), and Incremental Static Regeneration (ISR) using Apollo Client, primarily within a Next.js environment.

What is SSR, SSG, and ISR?

Why Server-Side Rendering Matters for GraphQL Apps

Implementing these rendering strategies with Apollo provides several key benefits. First, it drastically improves perceived performance because the browser receives HTML with data already injected, avoiding the "loading spinner" flash. Second, search engine crawlers can easily read the fully rendered content, boosting SEO. Finally, it reduces the amount of JavaScript shipped to the client, improving performance on low-powered devices.

Setting Up Apollo Client for SSR

To use Apollo across server and client environments, you need a client initialization function that can handle both contexts. The server requires a unique cache instance per request to prevent data leaking between users, while the client needs a persistent cache.

import { ApolloClient, InMemoryCache, HttpLink, NormalizedCacheObject } from '@apollo/client';
import fetch from 'cross-fetch';

let apolloClient: ApolloClient | undefined;

function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined',
    link: new HttpLink({
      uri: 'https://api.example.com/graphql',
      fetch,
    }),
    cache: new InMemoryCache(),
  });
}

export function initializeApollo(initialState: NormalizedCacheObject | null = null) {
  const _apolloClient = apolloClient ?? createApolloClient();

  if (initialState) {
    _apolloClient.cache.restore(initialState);
  }

  if (typeof window === 'undefined') {
    return _apolloClient;
  }

  if (!apolloClient) {
    apolloClient = _apolloClient;
  }

  return apolloClient;
}

Implementing SSR (Server-Side Rendering)

To implement SSR in Next.js, you use the getServerSideProps function. Here, you initialize a new Apollo Client, execute your GraphQL queries, and pass the extracted cache state to your page component as props.

import { gql } from '@apollo/client';
import { initializeApollo } from '../lib/apolloClient';

const GET_POSTS = gql`
  query GetPosts {
    posts {
      id
      title
    }
  }
`;

export async function getServerSideProps() {
  const apolloClient = initializeApollo();

  await apolloClient.query({
    query: GET_POSTS,
  });

  return {
    props: {
      initialApolloState: apolloClient.cache.extract(),
    },
  };
}

export default function Home({ initialApolloState }) {
  const apolloClient = initializeApollo(initialApolloState);
  const { data, loading } = useQuery(GET_POSTS);

  if (loading) return 
Loading...
; return ( <ul> {data.posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }

Implementing SSG (Static Site Generation)

For SSG, replace getServerSideProps with getStaticProps. The code is nearly identical, but Next.js will execute this function at build time and serve the resulting static HTML.

export async function getStaticProps() {
  const apolloClient = initializeApollo();

  await apolloClient.query({
    query: GET_POSTS,
  });

  return {
    props: {
      initialApolloState: apolloClient.cache.extract(),
    },
  };
}

If your page uses dynamic routes (e.g., /posts/[id]), you must also define a getStaticPaths function to tell Next.js which pages to pre-render at build time.

export async function getStaticPaths() {
  const apolloClient = initializeApollo();
  const { data } = await apolloClient.query({
    query: gql`query { posts { id } }`
  });

  return {
    paths: data.posts.map(post => ({ params: { id: post.id } })),
    fallback: 'blocking', // or true/false
  };
}

Implementing ISR (Incremental Static Regeneration)

ISR allows you to update static pages without rebuilding the entire site. You implement it by adding a revalidate property to the returned object in getStaticProps. The value represents the number of seconds after which a page re-generation can occur.

export async function getStaticProps() {
  const apolloClient = initializeApollo();

  await apolloClient.query({
    query: GET_POSTS,
  });

  return {
    props: {
      initialApolloState: apolloClient.cache.extract(),
    },
    // The page will be re-generated at most once every 60 seconds
    revalidate: 60, 
  };
}

When a request comes in after 60 seconds, the user will still be served the cached static page immediately. In the background, Next.js will regenerate the page using a fresh Apollo Client query. Once the background generation finishes, the new static page replaces the old one in the cache.

Best Practices for Apollo SSR

Conclusion

Integrating Apollo Client with server-side rendering strategies unlocks significant performance and SEO benefits for your GraphQL applications. By understanding when to use SSR for real-time data, SSG for static content, and ISR for periodically updated data, you can architect highly optimized web experiences. Remember to manage your Apollo cache carefully between the server and client to ensure a seamless hydration process, and always handle server-side data fetching errors to maintain application stability.

— Ad —

Google AdSense will appear here after approval

← Back to all articles