10 | Apollo is a GraphQL
11 | client that allows you to easily query the exact data you need from a
12 | GraphQL server. In addition to fetching and mutating data, Apollo
13 | analyzes your queries and their results to construct a client-side cache
14 | of your data, which is kept up to date as further queries and mutations
15 | are run, fetching more results from the server.
16 |
17 |
18 | In this simple example, we integrate Apollo seamlessly with{' '}
19 | Next by calling{' '}
20 |
21 | getStaticProps
22 | {' '}
23 | at our Page component. This approach lets us opt out of getInitialProps
24 | and let us use all the niceties provided by{' '}
25 | Next.
26 |
27 |
28 | On initial page load, while on the server and inside{' '}
29 |
30 | getStaticProps
31 |
32 | , we fetch the query used to get the list of posts. At the point in
33 | which the query promise resolves, our Apollo Client store is completely
34 | initialized. Then we serve the initial HTML with the fetched data and
35 | hydrate Apollo in the browser.
36 |
37 |
38 | This example relies on graph.cool for
39 | its GraphQL backend.
40 |
41 |
42 | >
43 | )
44 | AboutPage.Layout = MyLayout
45 |
46 | export default AboutPage
47 |
--------------------------------------------------------------------------------
/pages/client-only.js:
--------------------------------------------------------------------------------
1 | import App from '../components/App'
2 | import InfoBox from '../components/InfoBox'
3 | import Header from '../components/Header'
4 | import Submit from '../components/Submit'
5 | import PostList from '../components/PostList'
6 | import MyLayout from '../layouts/MyLayout'
7 |
8 | const ClientOnlyPage = (props) => (
9 | <>
10 |
11 | ℹ️ This page shows how use Apollo only in the client. If you{' '}
12 | reload this page, you will see a loader since
13 | Apollo didn't fetch any data on the server. This is useful when the page
14 | doesn't have SEO requirements or blocking data fetching requirements.
15 |
16 |
17 |
18 | >
19 | )
20 |
21 | ClientOnlyPage.Layout = MyLayout
22 |
23 | export default ClientOnlyPage
24 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import InfoBox from '../components/InfoBox'
2 | import Submit from '../components/Submit'
3 | import PostList, {
4 | ALL_POSTS_QUERY,
5 | allPostsQueryVars,
6 | } from '../components/PostList'
7 | import { initializeApollo, addApolloState } from '../lib/apolloClient'
8 | import styled from 'styled-components'
9 | import MyLayout from '../layouts/MyLayout'
10 | import Link from 'next/link'
11 |
12 | const Title = styled.h1`
13 | font-size: 22px;
14 | color: ${({ theme }) => theme.palette.secondary.main};
15 | `
16 |
17 | const IndexPage = () => (
18 | <>
19 | Home page
20 | Client Only
21 |
22 | ℹ️ This page shows how to use SSG with Apollo.
23 |
24 |
25 | >
26 | )
27 |
28 | export async function getStaticProps() {
29 | const apolloClient = initializeApollo()
30 |
31 | await apolloClient.query({
32 | query: ALL_POSTS_QUERY,
33 | variables: allPostsQueryVars,
34 | })
35 |
36 | return addApolloState(apolloClient, {
37 | props: {},
38 | revalidate: 1,
39 | })
40 | }
41 |
42 | IndexPage.Layout = MyLayout
43 |
44 | export default IndexPage
45 |
--------------------------------------------------------------------------------
/pages/material.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Container from '@material-ui/core/Container'
3 | import Typography from '@material-ui/core/Typography'
4 | import { Button, Paper } from '@material-ui/core'
5 | import styled from 'styled-components'
6 | import App from '../components/App'
7 | import Header from '../components/Header'
8 | import MyLayout from '../layouts/MyLayout'
9 |
10 | const MaterialContainer = styled(Container)`
11 | background-color: ${({ theme }) => theme.palette.secondary.main};
12 | margin-top: 40px;
13 | padding: 40px;
14 | `
15 |
16 | export default function Index() {
17 | return (
18 | <>
19 |
20 |
21 |
22 |
25 |
28 |
31 |
34 |
35 |
36 | >
37 | )
38 | }
39 | Index.Layout = MyLayout
40 |
--------------------------------------------------------------------------------
/pages/mobx-ssr.tsx:
--------------------------------------------------------------------------------
1 | import Clock from '../components/Clock'
2 |
3 | export default function MobXSSR() {
4 | return
5 | }
6 |
7 | // The date returned here will be different for every request that hits the page,
8 | // that is because the page becomes a serverless function instead of being statically
9 | // exported when you use `getServerSideProps` or `getInitialProps`
10 | export function getServerSideProps() {
11 | return { props: { initialState: { lastUpdate: Date.now() } } }
12 | }
13 |
--------------------------------------------------------------------------------
/store.ts:
--------------------------------------------------------------------------------
1 | import { action, observable, computed, runInAction, makeObservable } from 'mobx'
2 | import { enableStaticRendering } from 'mobx-react'
3 | import { useMemo } from 'react'
4 | // eslint-disable-next-line react-hooks/rules-of-hooks
5 | enableStaticRendering(typeof window === 'undefined')
6 |
7 | let store
8 |
9 | class Store {
10 | constructor() {
11 | makeObservable(this)
12 | }
13 |
14 | @observable lastUpdate = 0
15 | @observable light = false
16 |
17 | @action start = () => {
18 | this.timer = setInterval(() => {
19 | runInAction(() => {
20 | this.lastUpdate = Date.now()
21 | this.light = true
22 | })
23 | }, 1000)
24 | }
25 |
26 | @computed get timeString() {
27 | const pad = (n) => (n < 10 ? `0${n}` : n)
28 | const format = (t) =>
29 | `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad(
30 | t.getUTCSeconds()
31 | )}`
32 | return format(new Date(this.lastUpdate))
33 | }
34 |
35 | stop = () => clearInterval(this.timer)
36 |
37 | hydrate = (data) => {
38 | if (!data) return
39 |
40 | this.lastUpdate = data.lastUpdate !== null ? data.lastUpdate : Date.now()
41 | this.light = !!data.light
42 | }
43 | }
44 |
45 | function initializeStore(initialData = null) {
46 | const _store = store ?? new Store()
47 |
48 | // If your page has Next.js data fetching methods that use a Mobx store, it will
49 | // get hydrated here, check `pages/ssg.js` and `pages/ssr.js` for more details
50 | if (initialData) {
51 | _store.hydrate(initialData)
52 | }
53 | // For SSG and SSR always create a new store
54 | if (typeof window === 'undefined') return _store
55 | // Create the store once in the client
56 | if (!store) store = _store
57 |
58 | return _store
59 | }
60 |
61 | export function useStore(initialState) {
62 | const store = useMemo(() => initializeStore(initialState), [initialState])
63 | return store
64 | }
65 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": false,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "preserve"
20 | },
21 | "include": [
22 | "next-env.d.ts",
23 | "**/*.ts",
24 | "**/*.tsx"
25 | ],
26 | "exclude": [
27 | "node_modules"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------