Server-side Rendering
Prerequisites
An Angular project with server-side rendering enabled.
Overview
Apollo Orbit supports server-side rendering in two ways:
- It uses Angular's
HttpClient
under the hood which automatically supports server-side rendering as explained here. - 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.
- Standalone
- Module
Update appConfig
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
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 AppModule
import { provideHttpClient, withFetch } from '@angular/common/http';
import { provideClientHydration, withHttpTransferCacheOptions } from '@angular/platform-browser';
@NgModule({
...
providers: [
provideHttpClient(withFetch()),
provideClientHydration(withHttpTransferCacheOptions({
includePostRequests: true
}))
]
})
export class AppModule { }
Passing includePostRequests
to withHttpTransferCacheOptions
is required for default GraphQL setup where POST requests are used.
Update GraphQLModule
import { isPlatformBrowser } from '@angular/common';
import { Inject, NgModule, PLATFORM_ID, TransferState, inject, 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');
@NgModule({
providers: [
provideApolloOrbit(
withApolloOptions(() => {
const httpLinkFactory = inject(HttpLinkFactory);
const httpLink = httpLinkFactory.create({ uri: 'http://localhost:4000/graphql' });
const platformId = inject(PLATFORM_ID);
const ssrOptions = isPlatformBrowser(platformId)
? { ssrForceFetchDelay: 200 }
: { ssrMode: true };
return {
link: httpLink,
cache: new InMemoryCache(),
...ssrOptions
};
}),
withHttpLink()
)
]
})
export class GraphQLModule {
public constructor(
apollo: Apollo,
transferState: TransferState,
@Inject(PLATFORM_ID) platformId: { [key: string]: any }
) {
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.
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;