Skip to content

Instantly share code, notes, and snippets.

@luandevpro
Created August 17, 2019 06:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luandevpro/39ebbf1de3fdbd1445b421c812b80cc6 to your computer and use it in GitHub Desktop.
Save luandevpro/39ebbf1de3fdbd1445b421c812b80cc6 to your computer and use it in GitHub Desktop.
import fetch from 'isomorphic-fetch';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import { setContext } from 'apollo-link-context';
import { WebSocketLink } from 'apollo-link-ws';
let apolloClient = null;
// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
global.fetch = fetch;
}
const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'next-node-hasura-graphql.herokuapp.com';
const GRAPHQL_ENDPOINT = `https://${HASURA_GRAPHQL_ENGINE_HOSTNAME}/v1/graphql`;
const WEBSOCKET_ENDPOINT = `wss://${HASURA_GRAPHQL_ENGINE_HOSTNAME}/v1/graphql`;
function create(initialState, { getToken }) {
const authLink = setContext((_, { headers }) => {
const token = getToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
const wsLink = process.browser
? new WebSocketLink({
uri: WEBSOCKET_ENDPOINT,
options: {
lazy: true,
reconnect: true,
},
})
: null;
const httpLink = createHttpLink({
uri: GRAPHQL_ENDPOINT,
credentials: 'same-origin',
});
const link = process.browser
? split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httpLink,
)
: httpLink;
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser,
link: authLink.concat(link),
cache: new InMemoryCache().restore(initialState || {}),
});
}
function initApollo(initialState, options) {
if (!process.browser) {
return create(initialState, options);
}
if (!apolloClient) {
apolloClient = create(initialState, options);
}
return apolloClient;
}
export default initApollo;
import React from 'react';
import Head from 'next/head';
import PropTypes from 'prop-types';
import { getMarkupFromTree } from '@apollo/react-ssr';
import { ApolloProvider } from '@apollo/react-hooks';
import { renderToString } from 'react-dom/server';
import initApollo from './initApollo';
import { userByPk } from '../graphql/users/query';
let globalUser = null;
let globalToken = null;
function getCookie(context = {}) {
return context.req && context.req.signedCookies.token
? context.req.signedCookies.token
: globalToken;
}
export default (App) => {
class Apollo extends React.Component {
static displayName = 'withApollo(App)';
static propTypes = {
user: PropTypes.shape({
id: PropTypes.string,
isAdmin: PropTypes.bool,
}),
isFromServer: PropTypes.bool.isRequired,
token: PropTypes.string.isRequired,
};
static defaultProps = {
user: null,
};
constructor(props) {
super(props);
this.apolloClient = initApollo(props.apolloState, {
getToken: () => getCookie(),
});
}
componentDidMount() {
const { user, isFromServer, token } = this.props;
if (isFromServer) {
globalUser = user;
globalToken = token;
}
}
static async getInitialProps(ctx) {
const { Component, router } = ctx;
// Run all GraphQL queries in the component tree
// and extract the resulting data
const apollo = initApollo(
{},
{
getToken: () => getCookie(ctx),
},
);
const isFromServer = !!ctx.req;
let user = null;
// get currentUser if login for client vs server
if (ctx.req && ctx.req.signedCookies.token) {
try {
const { data } = await apollo.query({
query: userByPk,
});
user = ctx.req && ctx.req.signedCookies.token ? data.users[0] : globalUser;
} catch (e) {
console.log(e);
}
} else {
user = globalUser;
}
// get token from server vs client
const token =
ctx.req && ctx.req.signedCookies.token ? ctx.req.signedCookies.token : globalToken;
const appProps = { isFromServer, user, token };
if (App.getInitialProps) {
Object.assign(appProps, (await App.getInitialProps(ctx, apollo)) || {});
}
const url = {
ctx: ctx.asPath,
pathname: ctx.pathname,
query: ctx.query,
};
if (ctx.res && ctx.res.finished) {
return {};
}
if (ctx.req && ctx.req.signedCookies.token) {
try {
await getMarkupFromTree({
renderFunction: renderToString,
tree: (
<ApolloProvider ctx={ctx} {...appProps} client={apollo}>
<App
ctx={ctx}
{...appProps}
url={url}
Component={Component}
router={router}
apolloClient={apollo}
name="oc cho"
/>
</ApolloProvider>
),
});
} catch (error) {
console.error('Error while running `getMarkupFromTree`', error);
}
}
if (!process.browser) {
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
const apolloState = apollo.cache.extract();
return {
...appProps,
apolloState,
url,
};
}
render() {
return (
<ApolloProvider client={this.apolloClient}>
<App {...this.props} apolloClient={this.apolloClient} />
</ApolloProvider>
);
}
}
Apollo.propTypes = {
apolloState: PropTypes.object , //eslint-disable-line
};
return Apollo;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment