Skip to content

Commit

Permalink
Refactor Apollo GraphQL and use it with SSG pages (fetch GraphCMS API…
Browse files Browse the repository at this point in the history
… from getCommonStaticProps) and display customer label (work en/fr)
  • Loading branch information
Vadorequest committed May 3, 2020
1 parent 438529e commit d6533c4
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 28 deletions.
28 changes: 3 additions & 25 deletions src/hoc/withUniversalGraphQLDataLoader.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
import { getDataFromTree } from '@apollo/react-ssr';
import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
import { NormalizedCacheObject } from 'apollo-cache-inmemory';
import withApollo, { InitApolloOptions } from 'next-with-apollo';

// XXX This config is used on the FRONTEND (from the browser) or on the BACKEND (server), depending on whether it's loaded from SSR or client-side
const link = createHttpLink({
fetch, // Switches between unfetch & node-fetch for client & server.
uri: process.env.GRAPHQL_API_ENDPOINT,

// Headers applied here will be applied for all requests
// See the use of the "options" when running a graphQL query to specify options per-request at https://www.apollographql.com/docs/react/api/react-hooks/#options
headers: {
'gcms-locale-no-default': false,
'authorization': `Bearer ${process.env.GRAPHQL_API_KEY}`,
},
credentials: 'same-origin', // XXX See https://www.apollographql.com/docs/react/recipes/authentication#cookie
});
import { getStandaloneApolloClient } from '../utils/graphql';

/**
* Export a HOC from next-with-apollo
Expand All @@ -29,13 +13,7 @@ const link = createHttpLink({
*/
export default withApollo(
({ initialState }: InitApolloOptions<NormalizedCacheObject>) =>
new ApolloClient({
link: link,

// XXX Very important to provide the initialState, otherwise the client will replay the query upon loading,
// which is useless as the data were already fetched by the server (SSR)
cache: new InMemoryCache().restore(initialState || {}), // rehydrate the cache using the initial data passed from the server
}), {
getStandaloneApolloClient(initialState), {
getDataFromTree,
},
);
3 changes: 2 additions & 1 deletion src/pages/[locale]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ const HomePage: NextPage<Props> = (props): JSX.Element => {
>
{
(layoutPageProps: LayoutPageProps): JSX.Element => {
const { locale, lang } = props;
const { locale, lang, customer } = props;
console.log('layoutPageProps', layoutPageProps);
return (
<div>
<h1>Page: Home</h1>
<h2>Customer: {customer?.label}</h2>

<div>
Locale: {locale}<br />
Expand Down
3 changes: 2 additions & 1 deletion src/pages/[locale]/terms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ export const getStaticPaths: GetStaticPaths<StaticParams> = getCommonStaticPaths
type Props = {} & StaticProps;

const TermsPage: NextPage<Props> = (props): JSX.Element => {
const { locale, lang } = props;
const { locale, lang, customer } = props;
console.log('TermsPage props', props);

return (
<div>
<h1>Page: Terms</h1>
<h2>Customer: {customer?.label}</h2>

<div>
Locale: {locale}<br />
Expand Down
6 changes: 5 additions & 1 deletion src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ class NRNApp extends NextApp {
console.log('_app.render - App is ready, rendering...');
return (
<ApolloProvider client={apollo}>
<Component {...pageProps} err={err} />
<Component
{...pageProps}
err={err}
apollo={apollo}
/>
</ApolloProvider>
);
} else {
Expand Down
4 changes: 4 additions & 0 deletions src/types/StaticProps.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { I18nextResources } from '../utils/i18nextLocize';
import { NormalizedCacheObject } from 'apollo-cache-inmemory';
import { Customer } from './data/Customer';

/**
* Static properties returned by getStaticProps for static pages (using SSG)
*/
export type StaticProps = {
bestCountryCodes: string[];
customer: Customer;
customerRef: string;
defaultLocales: I18nextResources;
err?: Error; // Only defined if there was an error
Expand All @@ -14,4 +17,5 @@ export type StaticProps = {
lang: string;
locale: string;
statusCode?: number;
apolloStaticCache: NormalizedCacheObject,
};
39 changes: 39 additions & 0 deletions src/utils/SSG.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { ApolloQueryResult } from 'apollo-client';
import map from 'lodash.map';
import { GetStaticPaths, GetStaticProps } from 'next';
import { LAYOUT_QUERY } from '../gql/common/layoutQuery';

import { allowedLocales } from '../i18nConfig';
import { Customer } from '../types/data/Customer';
import { StaticParams } from '../types/StaticParams';
import { StaticProps } from '../types/StaticProps';
import { prepareGraphCMSLocaleHeader } from './graphcms';
import { getStandaloneApolloClient } from './graphql';
import { resolveFallbackLanguage } from './i18n';
import { fetchTranslations, I18nextResources } from './i18nextLocize';

Expand Down Expand Up @@ -58,15 +62,50 @@ export const getCommonStaticProps: GetStaticProps<StaticProps, StaticParams> = a
const bestCountryCodes: string[] = [lang, resolveFallbackLanguage(lang)];
const gcmsLocales: string = prepareGraphCMSLocaleHeader(bestCountryCodes);
const defaultLocales: I18nextResources = await fetchTranslations(lang); // Pre-fetches translations from Locize API
const apolloClient = getStandaloneApolloClient();
const variables = {
customerRef,
};
const queryOptions = {
displayName: 'LAYOUT_QUERY',
query: LAYOUT_QUERY,
variables,
context: {
headers: {
'gcms-locale': gcmsLocales,
},
},
};

const {
data,
errors,
loading,
networkStatus,
stale,
}: ApolloQueryResult<{
customer: Customer;
}> = await apolloClient.query(queryOptions);

if (errors) {
console.error(errors);
throw new Error('Errors were detected in GraphQL query.');
}

const {
customer,
} = data || {}; // XXX Use empty object as fallback, to avoid app crash when destructuring, if no data is returned

return {
props: {
customer,
lang,
locale,
customerRef,
bestCountryCodes,
gcmsLocales,
defaultLocales,
apolloStaticCache: apolloClient.cache.extract(),
isStaticRendering: true,
isReadyToRender: true,
},
Expand Down
27 changes: 27 additions & 0 deletions src/utils/graphql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';

const link = createHttpLink({
fetch, // Switches between unfetch & node-fetch for client & server.
uri: process.env.GRAPHQL_API_ENDPOINT,

// Headers applied here will be applied for all requests
// See the use of the "options" when running a graphQL query to specify options per-request at https://www.apollographql.com/docs/react/api/react-hooks/#options
headers: {
'gcms-locale-no-default': false,
'authorization': `Bearer ${process.env.GRAPHQL_API_KEY}`,
},
credentials: 'same-origin', // XXX See https://www.apollographql.com/docs/react/recipes/authentication#cookie
});

export const getStandaloneApolloClient = (initialState = {}): ApolloClient<NormalizedCacheObject> => {
return new ApolloClient({
link: link,

// XXX Very important to provide the initialState, otherwise the client will replay the query upon loading,
// which is useless as the data were already fetched by the server (SSR)
cache: new InMemoryCache().restore(initialState || {}), // Rehydrate the cache using the initial data passed from the server
});
};

0 comments on commit d6533c4

Please sign in to comment.