mirror of
https://github.com/sergeyyarkov/next-apollo-graphql-setup.git
synced 2026-10-11 10:19:43 +03:00
♻️ spaces
This commit is contained in:
parent
aff870be16
commit
c5fb489720
@ -7,6 +7,6 @@ const ARTICLES_QUERY = gql`
|
||||
title
|
||||
}
|
||||
}
|
||||
`;
|
||||
`
|
||||
|
||||
export default ARTICLES_QUERY
|
||||
@ -2,25 +2,25 @@ import Link from 'next/link'
|
||||
import classes from './Header.module.scss'
|
||||
|
||||
const Header = () => (
|
||||
<header className={classes.Header}>
|
||||
<ul>
|
||||
<li>
|
||||
<Link href='/'>
|
||||
<a>Index</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href='/about'>
|
||||
<a>About</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href='/articles'>
|
||||
<a>Articles</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
<header className={classes.Header}>
|
||||
<ul>
|
||||
<li>
|
||||
<Link href='/'>
|
||||
<a>Index</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href='/about'>
|
||||
<a>About</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href='/articles'>
|
||||
<a>Articles</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
)
|
||||
|
||||
export default Header
|
||||
@ -1,14 +1,14 @@
|
||||
import Header from '../Header/Header'
|
||||
|
||||
const Layout = props => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main>
|
||||
{props.children}
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main>
|
||||
{props.children}
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
@ -1,17 +1,19 @@
|
||||
import { useQuery } from '@apollo/react-hooks'
|
||||
|
||||
const Query = ({ children, query, id }) => {
|
||||
const { data, loading, error } = useQuery(query, {
|
||||
variables: {id}
|
||||
})
|
||||
const { data, loading, error } = useQuery(query, {
|
||||
variables: { id }
|
||||
})
|
||||
|
||||
if (loading) return (
|
||||
<div className="uk-container">
|
||||
<p>loading...</p>
|
||||
</div>
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="uk-container">
|
||||
<p>loading...</p>
|
||||
</div>
|
||||
)
|
||||
if (error) return <p>Error: {JSON.stringify(error)}</p>
|
||||
return children({ data })
|
||||
}
|
||||
if (error) return <p>Error: {JSON.stringify(error)}</p>
|
||||
return children({ data })
|
||||
}
|
||||
|
||||
export default Query
|
||||
@ -5,13 +5,13 @@ import Head from 'next/head'
|
||||
*/
|
||||
|
||||
const SEO = props => {
|
||||
const { pageTitle } = props
|
||||
const { pageTitle } = props
|
||||
|
||||
return (
|
||||
<Head>
|
||||
<title>{pageTitle}</title>
|
||||
</Head>
|
||||
)
|
||||
return (
|
||||
<Head>
|
||||
<title>{pageTitle}</title>
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
||||
export default SEO
|
||||
182
lib/apollo.js
182
lib/apollo.js
@ -6,9 +6,9 @@ import fetch from 'isomorphic-unfetch'
|
||||
|
||||
const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:3000/api/graphql'
|
||||
|
||||
let apolloClient = null
|
||||
let apolloClient = null
|
||||
|
||||
/**
|
||||
/**
|
||||
* Creates and provides the apolloContext
|
||||
* to a next.js PageTree. Use it by wrapping
|
||||
* your PageComponent via HOC pattern.
|
||||
@ -16,117 +16,117 @@ const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:3000/api/graphq
|
||||
* @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>
|
||||
)
|
||||
}
|
||||
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 =
|
||||
// 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 (displayName === 'App') {
|
||||
console.warn('This withApollo HOC only works with PageComponents.')
|
||||
}
|
||||
|
||||
// Allow Next.js to remove getInitialProps from the browser build
|
||||
if (typeof window === 'undefined') {
|
||||
if (ssr) {
|
||||
WithApollo.getInitialProps = async ctx => {
|
||||
const { AppTree } = ctx
|
||||
WithApollo.displayName = `withApollo(${displayName})`
|
||||
}
|
||||
|
||||
let pageProps = {}
|
||||
if (PageComponent.getInitialProps) {
|
||||
pageProps = await PageComponent.getInitialProps(ctx)
|
||||
}
|
||||
// Allow Next.js to remove getInitialProps from the browser build
|
||||
if (typeof window === 'undefined') {
|
||||
if (ssr) {
|
||||
WithApollo.getInitialProps = async ctx => {
|
||||
const { AppTree } = ctx
|
||||
|
||||
// Run all GraphQL queries in the component tree
|
||||
// and extract the resulting data
|
||||
const apolloClient = initApolloClient()
|
||||
let pageProps = {}
|
||||
if (PageComponent.getInitialProps) {
|
||||
pageProps = await PageComponent.getInitialProps(ctx)
|
||||
}
|
||||
|
||||
try {
|
||||
// Run all GraphQL queries
|
||||
await require('@apollo/react-ssr').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)
|
||||
}
|
||||
// Run all GraphQL queries in the component tree
|
||||
// and extract the resulting data
|
||||
const apolloClient = initApolloClient()
|
||||
|
||||
// getDataFromTree does not call componentWillUnmount
|
||||
// head side effect therefore need to be cleared manually
|
||||
Head.rewind()
|
||||
try {
|
||||
// Run all GraphQL queries
|
||||
await require('@apollo/react-ssr').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)
|
||||
}
|
||||
|
||||
// Extract query data from the Apollo store
|
||||
const apolloState = apolloClient.cache.extract()
|
||||
// getDataFromTree does not call componentWillUnmount
|
||||
// head side effect therefore need to be cleared manually
|
||||
Head.rewind()
|
||||
|
||||
return {
|
||||
...pageProps,
|
||||
apolloState
|
||||
}
|
||||
// Extract query data from the Apollo store
|
||||
const apolloState = apolloClient.cache.extract()
|
||||
|
||||
return {
|
||||
...pageProps,
|
||||
apolloState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WithApollo
|
||||
}
|
||||
|
||||
/**
|
||||
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
|
||||
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 = {}) {
|
||||
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
|
||||
const isBrowser = typeof window !== 'undefined'
|
||||
return new ApolloClient({
|
||||
connectToDevTools: isBrowser,
|
||||
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
|
||||
link: new HttpLink({
|
||||
uri: GRAPHQL_URL, // Server URL (must be absolute)
|
||||
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
|
||||
// Use fetch() polyfill on the server
|
||||
fetch: !isBrowser && fetch
|
||||
}),
|
||||
cache: new InMemoryCache().restore(initialState)
|
||||
})
|
||||
}
|
||||
function createApolloClient (initialState = {}) {
|
||||
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
|
||||
const isBrowser = typeof window !== 'undefined'
|
||||
return new ApolloClient({
|
||||
connectToDevTools: isBrowser,
|
||||
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
|
||||
link: new HttpLink({
|
||||
uri: GRAPHQL_URL, // Server URL (must be absolute)
|
||||
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
|
||||
// Use fetch() polyfill on the server
|
||||
fetch: !isBrowser && fetch
|
||||
}),
|
||||
cache: new InMemoryCache().restore(initialState)
|
||||
})
|
||||
}
|
||||
|
||||
1556
package-lock.json
generated
1556
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -21,5 +21,15 @@
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"react": "16.13.0",
|
||||
"react-dom": "16.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-node": "^4.0.0",
|
||||
"eslint-config-standard-jsx": "^8.1.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-node": "^11.0.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.18.3",
|
||||
"eslint-plugin-standard": "^4.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import '../assets/css/global.scss'
|
||||
|
||||
export default function Application({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
export default function Application ({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
class CustomDocument extends Document {
|
||||
render() {
|
||||
return (
|
||||
<Html lang='en'>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<Html lang='en'>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomDocument
|
||||
@ -1,11 +1,11 @@
|
||||
import { gql, ApolloServer } from 'apollo-server-micro'
|
||||
|
||||
const Articles = [
|
||||
{id: 1, title: 'Article 1'},
|
||||
{id: 2, title: 'Article 2'},
|
||||
{id: 3, title: 'Article 3'},
|
||||
{id: 4, title: 'Article 4'},
|
||||
{id: 5, title: 'Article 5'}
|
||||
{ id: 1, title: 'Article 1' },
|
||||
{ id: 2, title: 'Article 2' },
|
||||
{ id: 3, title: 'Article 3' },
|
||||
{ id: 4, title: 'Article 4' },
|
||||
{ id: 5, title: 'Article 5' }
|
||||
]
|
||||
|
||||
const typeDefs = gql`
|
||||
@ -17,27 +17,16 @@ const typeDefs = gql`
|
||||
id: ID
|
||||
title: String
|
||||
}
|
||||
`;
|
||||
`
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
articles: (_parent, _args, _context) => Articles
|
||||
}
|
||||
};
|
||||
|
||||
const apolloServer = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers
|
||||
});
|
||||
|
||||
const handler = apolloServer.createHandler({
|
||||
path: "/api/graphql"
|
||||
});
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false
|
||||
}
|
||||
Query: {
|
||||
articles: (_parent, _args, _context) => Articles
|
||||
}
|
||||
}
|
||||
|
||||
const apolloServer = new ApolloServer({ typeDefs, resolvers })
|
||||
const handler = apolloServer.createHandler({ path: '/api/graphql' })
|
||||
|
||||
export const config = { api: { bodyParser: false } }
|
||||
export default handler
|
||||
@ -1,36 +1,34 @@
|
||||
import Layout from '../components/Layout/Layout'
|
||||
import SEO from '../components/SEO/SEO'
|
||||
import Query from '../components/Query/query'
|
||||
import ARTICLES_QUERY from '../apollo/queries/articles/articles';
|
||||
import { withApollo } from '../lib/apollo';
|
||||
|
||||
import ARTICLES_QUERY from '../apollo/queries/articles/articles'
|
||||
import { withApollo } from '../lib/apollo'
|
||||
|
||||
const Articles = () => {
|
||||
return (
|
||||
<>
|
||||
<SEO pageTitle='Articles page' />
|
||||
<Layout>
|
||||
<h1>Articles page</h1>
|
||||
<div>
|
||||
<Query query={ARTICLES_QUERY}>
|
||||
{({ data: { articles } }) => {
|
||||
return articles.map(art => {
|
||||
return (
|
||||
<div key={art.id}>
|
||||
<p>
|
||||
title: {art.title} <br />
|
||||
id: {art.id}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}}
|
||||
</Query>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEO pageTitle='Articles page' />
|
||||
<Layout>
|
||||
<h1>Articles page</h1>
|
||||
<div>
|
||||
<Query query={ARTICLES_QUERY}>
|
||||
{({ data: { articles } }) => {
|
||||
return articles.map(art => {
|
||||
return (
|
||||
<div key={art.id}>
|
||||
<p>
|
||||
title: {art.title} <br />
|
||||
id: {art.id}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}}
|
||||
</Query>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default withApollo(Articles)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user