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
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
||||||
export default ARTICLES_QUERY
|
export default ARTICLES_QUERY
|
||||||
|
|||||||
@ -2,25 +2,25 @@ import Link from 'next/link'
|
|||||||
import classes from './Header.module.scss'
|
import classes from './Header.module.scss'
|
||||||
|
|
||||||
const Header = () => (
|
const Header = () => (
|
||||||
<header className={classes.Header}>
|
<header className={classes.Header}>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<Link href='/'>
|
<Link href='/'>
|
||||||
<a>Index</a>
|
<a>Index</a>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href='/about'>
|
<Link href='/about'>
|
||||||
<a>About</a>
|
<a>About</a>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href='/articles'>
|
<Link href='/articles'>
|
||||||
<a>Articles</a>
|
<a>Articles</a>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default Header
|
export default Header
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import Header from '../Header/Header'
|
import Header from '../Header/Header'
|
||||||
|
|
||||||
const Layout = props => {
|
const Layout = props => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
<main>
|
<main>
|
||||||
{props.children}
|
{props.children}
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Layout
|
export default Layout
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
import { useQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
|
|
||||||
const Query = ({ children, query, id }) => {
|
const Query = ({ children, query, id }) => {
|
||||||
const { data, loading, error } = useQuery(query, {
|
const { data, loading, error } = useQuery(query, {
|
||||||
variables: {id}
|
variables: { id }
|
||||||
})
|
})
|
||||||
|
|
||||||
if (loading) return (
|
if (loading) {
|
||||||
<div className="uk-container">
|
return (
|
||||||
<p>loading...</p>
|
<div className="uk-container">
|
||||||
</div>
|
<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
|
export default Query
|
||||||
|
|||||||
@ -5,13 +5,13 @@ import Head from 'next/head'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const SEO = props => {
|
const SEO = props => {
|
||||||
const { pageTitle } = props
|
const { pageTitle } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Head>
|
<Head>
|
||||||
<title>{pageTitle}</title>
|
<title>{pageTitle}</title>
|
||||||
</Head>
|
</Head>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SEO
|
export default SEO
|
||||||
|
|||||||
204
lib/apollo.js
204
lib/apollo.js
@ -6,9 +6,9 @@ import fetch from 'isomorphic-unfetch'
|
|||||||
|
|
||||||
const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:3000/api/graphql'
|
const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:3000/api/graphql'
|
||||||
|
|
||||||
let apolloClient = null
|
let apolloClient = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and provides the apolloContext
|
* Creates and provides the apolloContext
|
||||||
* to a next.js PageTree. Use it by wrapping
|
* to a next.js PageTree. Use it by wrapping
|
||||||
* your PageComponent via HOC pattern.
|
* 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 {Object} [config]
|
||||||
* @param {Boolean} [config.ssr=true]
|
* @param {Boolean} [config.ssr=true]
|
||||||
*/
|
*/
|
||||||
export function withApollo (PageComponent, { ssr = true } = {}) {
|
export function withApollo (PageComponent, { ssr = true } = {}) {
|
||||||
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
|
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
|
||||||
const client = useMemo(
|
const client = useMemo(
|
||||||
() => apolloClient || initApolloClient(apolloState),
|
() => apolloClient || initApolloClient(apolloState),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<PageComponent {...pageProps} />
|
<PageComponent {...pageProps} />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the correct displayName in development
|
// Set the correct displayName in development
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const displayName =
|
const displayName =
|
||||||
PageComponent.displayName || PageComponent.name || 'Component'
|
PageComponent.displayName || PageComponent.name || 'Component'
|
||||||
|
|
||||||
if (displayName === 'App') {
|
if (displayName === 'App') {
|
||||||
console.warn('This withApollo HOC only works with PageComponents.')
|
console.warn('This withApollo HOC only works with PageComponents.')
|
||||||
}
|
|
||||||
|
|
||||||
WithApollo.displayName = `withApollo(${displayName})`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow Next.js to remove getInitialProps from the browser build
|
WithApollo.displayName = `withApollo(${displayName})`
|
||||||
if (typeof window === 'undefined') {
|
}
|
||||||
if (ssr) {
|
|
||||||
WithApollo.getInitialProps = async ctx => {
|
// Allow Next.js to remove getInitialProps from the browser build
|
||||||
const { AppTree } = ctx
|
if (typeof window === 'undefined') {
|
||||||
|
if (ssr) {
|
||||||
let pageProps = {}
|
WithApollo.getInitialProps = async ctx => {
|
||||||
if (PageComponent.getInitialProps) {
|
const { AppTree } = ctx
|
||||||
pageProps = await PageComponent.getInitialProps(ctx)
|
|
||||||
}
|
let pageProps = {}
|
||||||
|
if (PageComponent.getInitialProps) {
|
||||||
// Run all GraphQL queries in the component tree
|
pageProps = await PageComponent.getInitialProps(ctx)
|
||||||
// and extract the resulting data
|
}
|
||||||
const apolloClient = initApolloClient()
|
|
||||||
|
// Run all GraphQL queries in the component tree
|
||||||
try {
|
// and extract the resulting data
|
||||||
// Run all GraphQL queries
|
const apolloClient = initApolloClient()
|
||||||
await require('@apollo/react-ssr').getDataFromTree(
|
|
||||||
<AppTree
|
try {
|
||||||
pageProps={{
|
// Run all GraphQL queries
|
||||||
...pageProps,
|
await require('@apollo/react-ssr').getDataFromTree(
|
||||||
apolloClient
|
<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
|
} catch (error) {
|
||||||
console.error('Error while running `getDataFromTree`', 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
|
||||||
// getDataFromTree does not call componentWillUnmount
|
console.error('Error while running `getDataFromTree`', error)
|
||||||
// head side effect therefore need to be cleared manually
|
}
|
||||||
Head.rewind()
|
|
||||||
|
// getDataFromTree does not call componentWillUnmount
|
||||||
// Extract query data from the Apollo store
|
// head side effect therefore need to be cleared manually
|
||||||
const apolloState = apolloClient.cache.extract()
|
Head.rewind()
|
||||||
|
|
||||||
return {
|
// Extract query data from the Apollo store
|
||||||
...pageProps,
|
const apolloState = apolloClient.cache.extract()
|
||||||
apolloState
|
|
||||||
}
|
return {
|
||||||
|
...pageProps,
|
||||||
|
apolloState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return WithApollo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return WithApollo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Always creates a new apollo client on the server
|
* Always creates a new apollo client on the server
|
||||||
* Creates or reuses apollo client in the browser.
|
* Creates or reuses apollo client in the browser.
|
||||||
* @param {Object} initialState
|
* @param {Object} initialState
|
||||||
*/
|
*/
|
||||||
function initApolloClient (initialState) {
|
function initApolloClient (initialState) {
|
||||||
// Make sure to create a new client for every server-side request so that data
|
// Make sure to create a new client for every server-side request so that data
|
||||||
// isn't shared between connections (which would be bad)
|
// isn't shared between connections (which would be bad)
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return createApolloClient(initialState)
|
return createApolloClient(initialState)
|
||||||
}
|
|
||||||
|
|
||||||
// Reuse client on the client-side
|
|
||||||
if (!apolloClient) {
|
|
||||||
apolloClient = createApolloClient(initialState)
|
|
||||||
}
|
|
||||||
|
|
||||||
return apolloClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Reuse client on the client-side
|
||||||
|
if (!apolloClient) {
|
||||||
|
apolloClient = createApolloClient(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apolloClient
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Creates and configures the ApolloClient
|
* Creates and configures the ApolloClient
|
||||||
* @param {Object} [initialState={}]
|
* @param {Object} [initialState={}]
|
||||||
*/
|
*/
|
||||||
function createApolloClient (initialState = {}) {
|
function createApolloClient (initialState = {}) {
|
||||||
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
|
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
|
||||||
const isBrowser = typeof window !== 'undefined'
|
const isBrowser = typeof window !== 'undefined'
|
||||||
return new ApolloClient({
|
return new ApolloClient({
|
||||||
connectToDevTools: isBrowser,
|
connectToDevTools: isBrowser,
|
||||||
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
|
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
|
||||||
link: new HttpLink({
|
link: new HttpLink({
|
||||||
uri: GRAPHQL_URL, // Server URL (must be absolute)
|
uri: GRAPHQL_URL, // Server URL (must be absolute)
|
||||||
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
|
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
|
||||||
// Use fetch() polyfill on the server
|
// Use fetch() polyfill on the server
|
||||||
fetch: !isBrowser && fetch
|
fetch: !isBrowser && fetch
|
||||||
}),
|
}),
|
||||||
cache: new InMemoryCache().restore(initialState)
|
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",
|
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||||
"react": "16.13.0",
|
"react": "16.13.0",
|
||||||
"react-dom": "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'
|
import '../assets/css/global.scss'
|
||||||
|
|
||||||
export default function Application({ Component, pageProps }) {
|
export default function Application ({ Component, pageProps }) {
|
||||||
return <Component {...pageProps} />
|
return <Component {...pageProps} />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||||
|
|
||||||
class CustomDocument extends Document {
|
class CustomDocument extends Document {
|
||||||
render() {
|
render () {
|
||||||
return (
|
return (
|
||||||
<Html lang='en'>
|
<Html lang='en'>
|
||||||
<Head />
|
<Head />
|
||||||
<body>
|
<body>
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CustomDocument
|
export default CustomDocument
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { gql, ApolloServer } from 'apollo-server-micro'
|
import { gql, ApolloServer } from 'apollo-server-micro'
|
||||||
|
|
||||||
const Articles = [
|
const Articles = [
|
||||||
{id: 1, title: 'Article 1'},
|
{ id: 1, title: 'Article 1' },
|
||||||
{id: 2, title: 'Article 2'},
|
{ id: 2, title: 'Article 2' },
|
||||||
{id: 3, title: 'Article 3'},
|
{ id: 3, title: 'Article 3' },
|
||||||
{id: 4, title: 'Article 4'},
|
{ id: 4, title: 'Article 4' },
|
||||||
{id: 5, title: 'Article 5'}
|
{ id: 5, title: 'Article 5' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const typeDefs = gql`
|
const typeDefs = gql`
|
||||||
@ -17,27 +17,16 @@ const typeDefs = gql`
|
|||||||
id: ID
|
id: ID
|
||||||
title: String
|
title: String
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
||||||
const resolvers = {
|
const resolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
articles: (_parent, _args, _context) => Articles
|
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
|
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 Layout from '../components/Layout/Layout'
|
||||||
import SEO from '../components/SEO/SEO'
|
import SEO from '../components/SEO/SEO'
|
||||||
import Query from '../components/Query/query'
|
import Query from '../components/Query/query'
|
||||||
import ARTICLES_QUERY from '../apollo/queries/articles/articles';
|
import ARTICLES_QUERY from '../apollo/queries/articles/articles'
|
||||||
import { withApollo } from '../lib/apollo';
|
import { withApollo } from '../lib/apollo'
|
||||||
|
|
||||||
|
|
||||||
const Articles = () => {
|
const Articles = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SEO pageTitle='Articles page' />
|
<SEO pageTitle='Articles page' />
|
||||||
<Layout>
|
<Layout>
|
||||||
<h1>Articles page</h1>
|
<h1>Articles page</h1>
|
||||||
<div>
|
<div>
|
||||||
<Query query={ARTICLES_QUERY}>
|
<Query query={ARTICLES_QUERY}>
|
||||||
{({ data: { articles } }) => {
|
{({ data: { articles } }) => {
|
||||||
return articles.map(art => {
|
return articles.map(art => {
|
||||||
return (
|
return (
|
||||||
<div key={art.id}>
|
<div key={art.id}>
|
||||||
<p>
|
<p>
|
||||||
title: {art.title} <br />
|
title: {art.title} <br />
|
||||||
id: {art.id}
|
id: {art.id}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</Query>
|
</Query>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withApollo(Articles)
|
export default withApollo(Articles)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user