Skip to content

Instantly share code, notes, and snippets.

@d-dmytro
Last active February 15, 2022 11:13
Show Gist options
  • Save d-dmytro/5269b6cba5efea557ce35ad38778ec2e to your computer and use it in GitHub Desktop.
Save d-dmytro/5269b6cba5efea557ce35ad38778ec2e to your computer and use it in GitHub Desktop.
Types for the Apollo setup file from the Next's "with-apollo" example (https://github.com/zeit/next.js/tree/canary/examples/with-apollo). If you've got a suggestion how to improve this, please post it in the comments section.
import React from 'react';
import Head from 'next/head';
import { ApolloProvider } from '@apollo/react-hooks';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
import { NextPage } from 'next';
export type AppApolloCache = any;
let apolloClient: ApolloClient<AppApolloCache> | null = null;
interface ApolloInitialProps {
apolloState?: AppApolloCache;
}
interface ApolloProps extends ApolloInitialProps {
apolloClient?: ApolloClient<any>;
}
/**
* Creates and provides the apolloContext
* to a next.js PageTree. Use it by wrapping
* your PageComponent via HOC pattern.
* @param {Function|Class} PageComponent
* @param {Object} [config]
* @param {Boolean} [config.ssr=true]
*/
export function withApollo<PageProps extends object, InitialProps = PageProps>(
PageComponent: NextPage<PageProps, InitialProps>,
{ ssr = true } = {}
) {
const WithApollo: NextPage<
ApolloProps & PageProps,
ApolloInitialProps & InitialProps
> = ({ apolloClient, apolloState, ...pageProps }) => {
const client = apolloClient || initApolloClient(apolloState);
return (
<ApolloProvider client={client}>
<PageComponent {...(pageProps as PageProps)} />
</ApolloProvider>
);
};
// Set the correct displayName in development
if (process.env.NODE_ENV !== 'production') {
const displayName =
PageComponent.displayName || PageComponent.name || 'Component';
if (displayName === 'App') {
console.warn('This withApollo HOC only works with PageComponents.');
}
WithApollo.displayName = `withApollo(${displayName})`;
}
if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async ctx => {
const { AppTree } = ctx;
// Initialize ApolloClient, add it to the ctx object so
// we can use it in `PageComponent.getInitialProp`.
const apolloClient = (ctx.apolloClient = initApolloClient());
// Run wrapped getInitialProps methods
let pageProps = {} as InitialProps;
if (PageComponent.getInitialProps) {
pageProps = await PageComponent.getInitialProps(ctx);
}
// Only on the server:
if (typeof window === 'undefined') {
// When redirecting, the response is finished.
// No point in continuing to render
if (ctx.res && ctx.res.finished) {
return pageProps;
}
// Only if ssr is enabled
if (ssr) {
try {
// Run all GraphQL queries
const { getDataFromTree } = await import('@apollo/react-ssr');
await getDataFromTree(
<AppTree
pageProps={{
...pageProps,
apolloClient
}}
/>
);
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
console.error('Error while running `getDataFromTree`', error);
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
}
// Extract query data from the Apollo store
const apolloState = apolloClient.cache.extract();
return {
...pageProps,
apolloState
};
};
}
return WithApollo;
}
/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {Object} initialState
*/
function initApolloClient(initialState?: AppApolloCache) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
return createApolloClient(initialState);
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = createApolloClient(initialState);
}
return apolloClient;
}
/**
* Creates and configures the ApolloClient
* @param {Object} [initialState={}]
*/
function createApolloClient(initialState: AppApolloCache = {}) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient<AppApolloCache>({
ssrMode: typeof window === 'undefined', // Disables forceFetch on the server (so queries are only run once)
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
fetch
}),
cache: new InMemoryCache().restore(initialState)
});
}
@lakshyasharma14
Copy link

lakshyasharma14 commented Feb 15, 2022

Hi, I am using the same script with "@apollo/client": "^3.5.8" and using @apollo/client/react/ssr instead of @apollo/react-ssr . But somehow the after const { AppTree } = ctx; I console log the output of AppTree and I find no queries in it. So my I am not able to call the server-side. It is directly getting called from the client then because there was nothing in apolloState.

import { useQuery, gql } from '@apollo/client';
import { withApollo } from '../lib/apollo/withApollo';

const MyQuery = gql`
  query MyQuery {
    trip {
      id
    }
  }
`;
const Sample = () => {
  const { loading, error, data } = useQuery(MyQuery);

  if (loading) {
    return <div>Loading...</div>;
  }
  if (error) {
    console.error(error);
    return <div>Error!</div>;
  }

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};
export default withApollo({ ssr: true })(Sample);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment