Skip to main content

Server-side Rendering

Prerequisites

An Angular project with server-side rendering enabled.

Overview

Apollo Orbit supports server-side rendering in two ways:

  1. It uses Angular's HttpClient under the hood which automatically supports server-side rendering as explained here.
  2. It uses Angular's TransferState to transfer Apollo Client's cache from server to client.

Setup

Setup SSR based on your angular project's style.

Update appConfig

app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideGraphQL } from './graphql/graphql.provider';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { provideClientHydration, withHttpTransferCacheOptions } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideGraphQL()
provideHttpClient(withFetch()),
provideClientHydration(withHttpTransferCacheOptions({
includePostRequests: true
}))
]
};

Passing includePostRequests to withHttpTransferCacheOptions is required for default GraphQL setup where POST requests are used.

Update provideGraphQL

app/graphql/graphql.provider.ts
import { isPlatformBrowser } from '@angular/common';
import { ENVIRONMENT_INITIALIZER, EnvironmentProviders, PLATFORM_ID, TransferState, inject, makeEnvironmentProviders, makeStateKey } from '@angular/core';
import { Apollo, InMemoryCache, provideApolloOrbit, withApolloOptions } from '@apollo-orbit/angular';
import { HttpLinkFactory, withHttpLink } from '@apollo-orbit/angular/http';
import { NormalizedCacheObject } from '@apollo/client/core';

const APOLLO_STATE_KEY = makeStateKey<NormalizedCacheObject>('APOLLO_STATE');

export function provideGraphQL(): EnvironmentProviders {
return makeEnvironmentProviders([
provideApolloOrbit(
withApolloOptions(() => {
const httpLinkFactory = inject(HttpLinkFactory);
const httpLink = httpLinkFactory.create({ uri: 'http://localhost:4000/graphql' });
const platformId = inject<object>(PLATFORM_ID);

const ssrOptions = isPlatformBrowser(platformId)
? { ssrForceFetchDelay: 200 }
: { ssrMode: true };

return {
link: httpLink,
cache: new InMemoryCache(),
...ssrOptions
};
}),
withHttpLink()
),
{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useFactory: () => () => {
const apollo = inject(Apollo);
const transferState = inject(TransferState);
const platformId = inject(PLATFORM_ID);

if (isPlatformBrowser(platformId)) {
const state = transferState.get(APOLLO_STATE_KEY, undefined);
apollo.cache.restore(state);
} else {
transferState.onSerialize(APOLLO_STATE_KEY, () => apollo.cache.extract());
}
}
}
]);
}

Update main.server.ts

Apollo Client sets a 10s timer which outputs a message for installing Apollo Client Devtools extension in the browser's console. This timer can cause issues with server-side rendering, as it prevents the application from stabilising and causes delays in responding to user events on the page.
To prevent this issue, set the __DEV__ global variable to false in main.server.ts file, which instructs Apollo Client that the application is not running in development mode and thus the message is not queued.

src/main.server.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';

// Required for SSR to stabilise because of ApolloClient setting a 10s timeout for suggesting devtools in the console.
(globalThis as any).__DEV__ = false;

const bootstrap = () => bootstrapApplication(AppComponent, config);

export default bootstrap;