Skip to content

Commit

Permalink
Refactor api-routes-apollo-server-and-client example (vercel#8673)
Browse files Browse the repository at this point in the history
* Remove connectToDevTools in favour of better default

Disabled devtools in production

* Refactor api-routes-apollo-server-and-client example
  • Loading branch information
HaNdTriX authored and Luis Alvarez D committed Sep 12, 2019
1 parent 9731e7e commit 1045cd4
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 126 deletions.
151 changes: 151 additions & 0 deletions examples/api-routes-apollo-server-and-client/apollo/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React, { useMemo } from 'react'
import Head from 'next/head'
import { ApolloProvider } from '@apollo/react-hooks'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'

let apolloClient = null

/**
* 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 (PageComponent, { ssr = true } = {}) {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
const client = useMemo(
() => apolloClient || initApolloClient(apolloState),
[]
)
return (
<ApolloProvider client={client}>
<PageComponent {...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 = {}
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) {
// 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 = {}) {
const ssrMode = typeof window === 'undefined'
const cache = new InMemoryCache().restore(initialState)

// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient({
ssrMode,
link: createIsomorphLink(),
cache
})
}

function createIsomorphLink () {
if (typeof window === 'undefined') {
const { SchemaLink } = require('apollo-link-schema')
const { schema } = require('./schema')
return new SchemaLink({ schema })
} else {
const { HttpLink } = require('apollo-link-http')
return new HttpLink({
uri: '/api/graphql',
credentials: 'same-origin'
})
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const resolvers = {
Query: {
viewer (_parent, _args, _context, _info) {
return { name: 'John Smith', id: 1 }
return { id: 1, name: 'John Smith', status: 'cached' }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const typeDefs = gql`
type User {
id: ID!
name: String!
status: String!
}
type Query {
Expand Down
56 changes: 0 additions & 56 deletions examples/api-routes-apollo-server-and-client/lib/init-apollo.js

This file was deleted.

This file was deleted.

10 changes: 3 additions & 7 deletions examples/api-routes-apollo-server-and-client/pages/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import withApolloClient from '../lib/with-apollo-client'
import { withApollo } from '../apollo/client'
import gql from 'graphql-tag'
import Link from 'next/link'
import { useQuery } from '@apollo/react-hooks'
Expand All @@ -8,7 +8,7 @@ const ViewerQuery = gql`
viewer {
id
name
status @client
status
}
}
`
Expand All @@ -33,8 +33,4 @@ const Index = () => {
return null
}

Index.getInitialProps = async ({ client }) => {
await client.query({ query: ViewerQuery })
}

export default withApolloClient(Index)
export default withApollo(Index)

0 comments on commit 1045cd4

Please sign in to comment.