diff --git a/examples/with-apollo/README.md b/examples/with-apollo/README.md
index 2ed6389c72d947..069430a94c024d 100644
--- a/examples/with-apollo/README.md
+++ b/examples/with-apollo/README.md
@@ -1,15 +1,14 @@
# Apollo Example
-[Apollo](https://www.apollographql.com/client/) is a GraphQL client that allows you to easily query the exact data you need from a GraphQL server. In addition to fetching and mutating data, Apollo analyzes your queries and their results to construct a client-side cache of your data, which is kept up to date as further queries and mutations are run, fetching more results from the server.
+[Apollo](https://www.apollographql.com/client/) is a GraphQL client that allows you to easily query the exact data you need from a GraphQL server. In addition to fetching and mutating data, Apollo analyzes your queries and their results to construct a client-side cache of your data, which is kept up to date as further queries and mutations are run.
-In this simple example, we integrate Apollo seamlessly with Next by wrapping our `pages/index.js` inside a [higher-order component (HOC)](https://facebook.github.io/react/docs/higher-order-components.html). Using the HOC pattern we're able to pass down a central store of query result data created by Apollo into our React component hierarchy defined inside each page of our Next application.
-
-On initial page load, while on the server and inside `getInitialProps`, we invoke the Apollo method, [`getDataFromTree`](https://www.apollographql.com/docs/react/api/react-ssr/#getdatafromtree). This method returns a promise; at the point in which the promise resolves, our Apollo Client store is completely initialized.
+In this simple example, we integrate Apollo seamlessly with [Next.js data fetching methods](https://nextjs.org/docs/basic-features/data-fetching) to fetch queries in the server and hydrate them in the browser.
This example relies on [graph.cool](https://www.graph.cool) for its GraphQL backend.
-Note: Do not be alarmed that you see two renders being executed. Apollo recursively traverses the React render tree looking for Apollo query components. When it has done that, it fetches all these queries and then passes the result to a cache. This cache is then used to render the data on the server side (another React render).
-https://www.apollographql.com/docs/react/api/react-ssr/#getdatafromtree
+## Demo
+
+[https://next-with-apollo.now.sh](https://next-with-apollo.now.sh)
## Deploy your own
@@ -17,8 +16,6 @@ Deploy the example using [Vercel](https://vercel.com):
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-apollo)
-_Live Example: https://next-with-apollo.now.sh_
-
## How to use
### Using `create-next-app`
diff --git a/examples/with-apollo/apolloClient.js b/examples/with-apollo/apolloClient.js
deleted file mode 100644
index ef6cad28e29c33..00000000000000
--- a/examples/with-apollo/apolloClient.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ApolloClient } from 'apollo-client'
-import { InMemoryCache } from 'apollo-cache-inmemory'
-import { HttpLink } from 'apollo-link-http'
-
-export default function createApolloClient(initialState, ctx) {
- // The `ctx` (NextPageContext) will only be present on the server.
- // use it to extract auth headers (ctx.req) or similar.
- return new ApolloClient({
- ssrMode: Boolean(ctx),
- 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`
- }),
- cache: new InMemoryCache().restore(initialState),
- })
-}
diff --git a/examples/with-apollo/components/Header.js b/examples/with-apollo/components/Header.js
index 155efd8b8ee4a7..672365061d9bb7 100644
--- a/examples/with-apollo/components/Header.js
+++ b/examples/with-apollo/components/Header.js
@@ -1,33 +1,35 @@
+import { useRouter } from 'next/router'
import Link from 'next/link'
-import { withRouter } from 'next/router'
-const Header = ({ router: { pathname } }) => (
-
-)
+export default function Header() {
+ const { pathname } = useRouter()
-export default withRouter(Header)
+ return (
+
+ )
+}
diff --git a/examples/with-apollo/components/PostList.js b/examples/with-apollo/components/PostList.js
index 1c2a1f6d09511a..1b6bb57192b7b8 100644
--- a/examples/with-apollo/components/PostList.js
+++ b/examples/with-apollo/components/PostList.js
@@ -18,6 +18,7 @@ export const ALL_POSTS_QUERY = gql`
}
}
`
+
export const allPostsQueryVars = {
skip: 0,
first: 10,
diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js
deleted file mode 100644
index 9294339cc78c64..00000000000000
--- a/examples/with-apollo/lib/apollo.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import App from 'next/app'
-import Head from 'next/head'
-import { ApolloProvider } from '@apollo/react-hooks'
-import createApolloClient from '../apolloClient'
-
-// On the client, we store the Apollo Client in the following variable.
-// This prevents the client from reinitializing between page transitions.
-let globalApolloClient = null
-
-/**
- * Installs the Apollo Client on NextPageContext
- * or NextAppContext. Useful if you want to use apolloClient
- * inside getStaticProps, getStaticPaths or getServerSideProps
- * @param {NextPageContext | NextAppContext} ctx
- */
-export const initOnContext = (ctx) => {
- const inAppContext = Boolean(ctx.ctx)
-
- // We consider installing `withApollo({ ssr: true })` on global App level
- // as antipattern since it disables project wide Automatic Static Optimization.
- if (process.env.NODE_ENV === 'development') {
- if (inAppContext) {
- console.warn(
- 'Warning: You have opted-out of Automatic Static Optimization due to `withApollo` in `pages/_app`.\n' +
- 'Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n'
- )
- }
- }
-
- // Initialize ApolloClient if not already done
- const apolloClient =
- ctx.apolloClient ||
- initApolloClient(ctx.apolloState || {}, inAppContext ? ctx.ctx : ctx)
-
- // We send the Apollo Client as a prop to the component to avoid calling initApollo() twice in the server.
- // Otherwise, the component would have to call initApollo() again but this
- // time without the context. Once that happens, the following code will make sure we send
- // the prop as `null` to the browser.
- apolloClient.toJSON = () => null
-
- // Add apolloClient to NextPageContext & NextAppContext.
- // This allows us to consume the apolloClient inside our
- // custom `getInitialProps({ apolloClient })`.
- ctx.apolloClient = apolloClient
- if (inAppContext) {
- ctx.ctx.apolloClient = apolloClient
- }
-
- return ctx
-}
-
-/**
- * Always creates a new apollo client on the server
- * Creates or reuses apollo client in the browser.
- * @param {NormalizedCacheObject} initialState
- * @param {NextPageContext} ctx
- */
-const initApolloClient = (initialState, ctx) => {
- // 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, ctx)
- }
-
- // Reuse client on the client-side
- if (!globalApolloClient) {
- globalApolloClient = createApolloClient(initialState, ctx)
- }
-
- return globalApolloClient
-}
-
-/**
- * Creates a withApollo HOC
- * that provides the apolloContext
- * to a next.js Page or AppTree.
- * @param {Object} withApolloOptions
- * @param {Boolean} [withApolloOptions.ssr=false]
- * @returns {(PageComponent: ReactNode) => ReactNode}
- */
-export const withApollo = ({ ssr = false } = {}) => (PageComponent) => {
- const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
- let client
- if (apolloClient) {
- // Happens on: getDataFromTree & next.js ssr
- client = apolloClient
- } else {
- // Happens on: next.js csr
- client = initApolloClient(apolloState, undefined)
- }
-
- return (
-
-
-
- )
- }
-
- // Set the correct displayName in development
- if (process.env.NODE_ENV !== 'production') {
- const displayName =
- PageComponent.displayName || PageComponent.name || 'Component'
- WithApollo.displayName = `withApollo(${displayName})`
- }
-
- if (ssr || PageComponent.getInitialProps) {
- WithApollo.getInitialProps = async (ctx) => {
- const inAppContext = Boolean(ctx.ctx)
- const { apolloClient } = initOnContext(ctx)
-
- // Run wrapped getInitialProps methods
- let pageProps = {}
- if (PageComponent.getInitialProps) {
- pageProps = await PageComponent.getInitialProps(ctx)
- } else if (inAppContext) {
- pageProps = await App.getInitialProps(ctx)
- }
-
- // Only on the server:
- if (typeof window === 'undefined') {
- const { AppTree } = ctx
- // When redirecting, the response is finished.
- // No point in continuing to render
- if (ctx.res && ctx.res.finished) {
- return pageProps
- }
-
- // Only if dataFromTree is enabled
- if (ssr && AppTree) {
- try {
- // Import `@apollo/react-ssr` dynamically.
- // We don't want to have this in our client bundle.
- const { getDataFromTree } = await import('@apollo/react-ssr')
-
- // Since AppComponents and PageComponents have different context types
- // we need to modify their props a little.
- let props
- if (inAppContext) {
- props = { ...pageProps, apolloClient }
- } else {
- props = { pageProps: { ...pageProps, apolloClient } }
- }
-
- // Take the Next.js AppTree, determine which queries are needed to render,
- // and fetch them. This method can be pretty slow since it renders
- // your entire AppTree once for every query. Check out apollo fragments
- // if you want to reduce the number of rerenders.
- // https://www.apollographql.com/docs/react/data/fragments/
- await getDataFromTree()
- } 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()
- }
- }
-
- return {
- ...pageProps,
- // Extract query data from the Apollo store
- apolloState: apolloClient.cache.extract(),
- // Provide the client for ssr. As soon as this payload
- // gets JSON.stringified it will remove itself.
- apolloClient: ctx.apolloClient,
- }
- }
- }
-
- return WithApollo
-}
diff --git a/examples/with-apollo/lib/apolloClient.js b/examples/with-apollo/lib/apolloClient.js
new file mode 100644
index 00000000000000..77ee7a59b11494
--- /dev/null
+++ b/examples/with-apollo/lib/apolloClient.js
@@ -0,0 +1,38 @@
+import { useMemo } from 'react'
+import { ApolloClient } from 'apollo-client'
+import { InMemoryCache } from 'apollo-cache-inmemory'
+import { HttpLink } from 'apollo-link-http'
+
+let apolloClient
+
+function createApolloClient() {
+ return new ApolloClient({
+ ssrMode: typeof window === 'undefined',
+ 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`
+ }),
+ cache: new InMemoryCache(),
+ })
+}
+
+export function initializeApollo(initialState = null) {
+ const _apolloClient = apolloClient ?? createApolloClient()
+
+ // If your page has Next.js data fetching methods that use Apollo Client, the initial state
+ // get hydrated here
+ if (initialState) {
+ _apolloClient.cache.restore(initialState)
+ }
+ // For SSG and SSR always create a new Apollo Client
+ if (typeof window === 'undefined') return _apolloClient
+ // Create the Apollo Client once in the client
+ if (!apolloClient) apolloClient = _apolloClient
+
+ return _apolloClient
+}
+
+export function useApollo(initialState) {
+ const store = useMemo(() => initializeApollo(initialState), [initialState])
+ return store
+}
diff --git a/examples/with-apollo/package.json b/examples/with-apollo/package.json
index 5075f2c8650363..2138dc569365d7 100644
--- a/examples/with-apollo/package.json
+++ b/examples/with-apollo/package.json
@@ -1,6 +1,6 @@
{
"name": "with-apollo",
- "version": "2.0.0",
+ "version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
@@ -8,7 +8,6 @@
},
"dependencies": {
"@apollo/react-hooks": "3.1.3",
- "@apollo/react-ssr": "3.1.3",
"apollo-cache-inmemory": "1.6.5",
"apollo-client": "2.6.8",
"apollo-link-http": "1.5.16",
@@ -19,8 +18,8 @@
"react": "^16.7.0",
"react-dom": "^16.7.0"
},
- "license": "ISC",
"devDependencies": {
"babel-plugin-graphql-tag": "^2.5.0"
- }
+ },
+ "license": "ISC"
}
diff --git a/examples/with-apollo/pages/_app.js b/examples/with-apollo/pages/_app.js
new file mode 100644
index 00000000000000..216cff1c81197d
--- /dev/null
+++ b/examples/with-apollo/pages/_app.js
@@ -0,0 +1,12 @@
+import { ApolloProvider } from '@apollo/react-hooks'
+import { useApollo } from '../lib/apolloClient'
+
+export default function App({ Component, pageProps }) {
+ const apolloClient = useApollo(pageProps.initialApolloState)
+
+ return (
+
+
+
+ )
+}
diff --git a/examples/with-apollo/pages/about.js b/examples/with-apollo/pages/about.js
index eb88a80e41f3f5..3bdc6a843afff6 100644
--- a/examples/with-apollo/pages/about.js
+++ b/examples/with-apollo/pages/about.js
@@ -16,23 +16,23 @@ const AboutPage = () => (
In this simple example, we integrate Apollo seamlessly with{' '}
- Next by wrapping our
- Page component inside a{' '}
-
- higher-order component (HOC)
-
- . Using the HOC pattern we're able to pass down a central store of query
- result data created by Apollo into our React component hierarchy defined
- inside a page of our Next application.
+ Next by calling{' '}
+
+ getStaticProps
+ {' '}
+ at our Page component. This approach lets us opt out of getInitialProps
+ and let us use all the niceties provided by{' '}
+ Next.
- On initial page load, while on the server and inside getInitialProps, we
- invoke the Apollo method,{' '}
-
- getDataFromTree
+ On initial page load, while on the server and inside{' '}
+
+ getStaticProps
- . This method returns a promise; at the point in which the promise
- resolves, our Apollo Client store is completely initialized.
+ , we fetch the query used to get the list of posts. At the point in
+ which the query promise resolves, our Apollo Client store is completely
+ initialized. Then we serve the initial HTML with the fetched data and
+ hydrate Apollo in the browser.
This example relies on graph.cool for
diff --git a/examples/with-apollo/pages/client-only.js b/examples/with-apollo/pages/client-only.js
index 86045b36ed2fdb..29b450a1e36127 100644
--- a/examples/with-apollo/pages/client-only.js
+++ b/examples/with-apollo/pages/client-only.js
@@ -3,27 +3,19 @@ import InfoBox from '../components/InfoBox'
import Header from '../components/Header'
import Submit from '../components/Submit'
import PostList from '../components/PostList'
-import { withApollo } from '../lib/apollo'
const ClientOnlyPage = (props) => (
- ℹ️ This example shows how to disable apollos query fetching on the server.
- If you reload this page, you will see a loader
- since Apollo didn't fetch any data on the server. This allows{' '}
-
- automatic static optimization
-
- .
+ ℹ️ This page shows how use Apollo only in the client. If you{' '}
+ reload this page, you will see a loader since
+ Apollo didn't fetch any data on the server. This is useful when the page
+ doesn't have SEO requirements or blocking data fetching requirements.
)
-export default withApollo()(ClientOnlyPage)
+export default ClientOnlyPage
diff --git a/examples/with-apollo/pages/index.js b/examples/with-apollo/pages/index.js
index 93e8d42fecaac1..184cc530b508e0 100644
--- a/examples/with-apollo/pages/index.js
+++ b/examples/with-apollo/pages/index.js
@@ -2,28 +2,35 @@ import App from '../components/App'
import InfoBox from '../components/InfoBox'
import Header from '../components/Header'
import Submit from '../components/Submit'
-import PostList from '../components/PostList'
-import { withApollo } from '../lib/apollo'
+import PostList, {
+ ALL_POSTS_QUERY,
+ allPostsQueryVars,
+} from '../components/PostList'
+import { initializeApollo } from '../lib/apolloClient'
const IndexPage = () => (
-
- ℹ️ This example shows how to fetch all initial apollo queries on the
- server. If you reload this page you won't see a loader
- since Apollo fetched all needed data on the server. This prevents{' '}
-
- automatic static optimization
- {' '}
- in favour of full Server-Side-Rendering.
-
+ ℹ️ This page shows how to use SSG with Apollo.
)
-export default withApollo({ ssr: true })(IndexPage)
+export async function getStaticProps() {
+ const apolloClient = initializeApollo()
+
+ await apolloClient.query({
+ query: ALL_POSTS_QUERY,
+ variables: allPostsQueryVars,
+ })
+
+ return {
+ props: {
+ initialApolloState: apolloClient.cache.extract(),
+ },
+ unstable_revalidate: 1,
+ }
+}
+
+export default IndexPage