Skip to content

Instantly share code, notes, and snippets.

@richbai90
Last active January 12, 2020 01:30
Show Gist options
  • Save richbai90/98011174f66ed275d9531ea5813bf4a2 to your computer and use it in GitHub Desktop.
Save richbai90/98011174f66ed275d9531ea5813bf4a2 to your computer and use it in GitHub Desktop.
Next.js + ApolloClient + ApolloLinkState

I cannot take credit for this, most of the work was done by @admamsoffer and his great example repo that you can find here: https://github.com/zeit/next.js/tree/master/examples/with-apollo

My version adds ApolloLinkState to the mix, and was originally written in typescript. I've done my best to remove the types to make it more approachable.

The major change that I made was passing all the appProps to the app component in the with-apollo hoc, something I'm not actually sure is neccesary, and changing RederComponent to simply Component since RederComponent was undefined.

import {
ApolloClient,
ApolloLink,
HttpLink,
InMemoryCache,
NormalizedCacheObject,
} from 'apollo-boost';
import { withClientState } from 'apollo-link-state';
import fetch from 'isomorphic-unfetch';
import defaultState from './schema';
import { demoMutation } from './mutations';
let apolloClient = null;
// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
global.fetch = fetch;
}
function create(initialState) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
const cache = new InMemoryCache().restore(initialState || {});
const httpLink = new HttpLink({
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
});
const stateLink = withClientState({
cache,
defaults: defaultState,
resolvers: {
Mutation: {
toggleDragging,
},
},
});
return new ApolloClient({
cache,
connectToDevTools: process.browser,
link: ApolloLink.from([stateLink, httpLink]),
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
});
}
export default function initApollo(initialState) {
// 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 (!process.browser) {
return create(initialState);
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState);
}
return apolloClient;
}
import gql from 'graphql-tag';
export default gql`
query {
builder @client {
tree
map
}
}
`;
import Paper from "@material-ui/core/Paper";
import { withStyles } from "@material-ui/core/styles";
import { graphql } from "react-apollo";
import { compose } from 'recompose';
import getTree from './queries/getTree';
import styles from './styles';
const Canvas = ({
classes,
// activeItem,
...data
}) => {
console.log(data); // all the queried data
return(
<div className={classes.canvasBackground} id="canvas">
<Paper elevation={6} className={classes.canvas}>
{// todo }
</Paper>
</div>)
};
export default compose(
withStyles(styles),
graphql(getTree)
)(Canvas);
const tree = {
__typename: 'tree',
children: [
],
component: 'div',
id: 'PageRoot'
};
const defaultState = {
__typename: 'builder',
currentItem: '',
dragging: false,
map: {
__typename: 'map',
pageRoot: tree,
},
tree,
};
export default defaultState;
import pageAState from './pageAState';
export default {
pageAState,
}
import Head from 'next/head';
import { Component } from 'react';
import { getDataFromTree } from 'react-apollo';
import initApollo from './apollo-init';
const withApolloHoc = App => {
// this is a change from the example repo
let appProps;
return class Apollo extends Component {
public static displayName = 'withApollo(App)';
public static async getInitialProps(props) {
// this is a change from the example repo, RenderComponent is undefined
const { Component : RenderComponent, router } = props;
appProps = await App.getInitialProps(props);
// Run all GraphQL queries in the component tree
// and extract the resulting data
const apollo = initApollo();
if (!process.browser) {
try {
// Run all GraphQL queries
await getDataFromTree(
<App
{...appProps}
Component={RenderComponent}
router={router}
apolloClient={apollo}
/>,
);
} 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 = apollo.cache.extract();
return {
...appProps,
apolloState,
};
}
public apolloClient;
constructor(props) {
super(props);
this.apolloClient = initApollo(props.apolloState);
}
public render() {
// this is a change from the example repo, I don't know that it's required.
return <App {...this.props} {...appProps} apolloClient={this.apolloClient} />;
}
};
};
export default withApolloHoc;
@benpetsch
Copy link

Great work over here.
TODO: Remove 3x keyword "public" from with-apollo.jsx (lines 10, 11 and 49) and that's it.

@puneetv05
Copy link

Unable to implement the mutation in this example
Cannot read property 'cache' of null

Code Sample :

export const toggleLoading= (_,{ cache }) => {
            const query = gql`
            query signinState {
                signinState @client{
                    loading
                }
            } 
`;
...contd...

@puneetv05
Copy link

@richbai90 I am unable to create a mutation with your example!! Can't find how to pass cache in mutation file .

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