⚡ fixed SSR

This commit is contained in:
Sergey Yarkov 2020-03-08 15:23:52 +03:00
parent 1694936927
commit 63ddaa7f6e
9 changed files with 176 additions and 59 deletions

View File

@ -5,10 +5,10 @@ services:
restart: always restart: always
build: . build: .
ports: ports:
- '3000:3000' - '3000'
# webserver: webserver:
# container_name: webserver container_name: webserver
# restart: unless-stopped restart: unless-stopped
# build: nginx build: nginx
# ports: ports:
# - '80:80' - '80:80'

8
graphql/index.js Normal file
View File

@ -0,0 +1,8 @@
import { makeExecutableSchema } from 'graphql-tools'
import { typeDefs } from './typeDefs'
import { resolvers } from './resolvers'
export const schema = makeExecutableSchema({
typeDefs,
resolvers
})

View File

@ -1,13 +1,148 @@
import { withData } from 'next-apollo' import React from 'react'
import { HttpLink } from 'apollo-boost' import Head from 'next/head'
import { ApolloProvider } from '@apollo/react-hooks'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
const config = { let globalApolloClient = null
link: new HttpLink({
uri: process.env.GRAPHQL_API_URI, /**
opts: { * Creates and provides the apolloContext
credentials: 'same-origin' * 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 = 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 (!globalApolloClient) {
globalApolloClient = createApolloClient(initialState)
}
return globalApolloClient
}
/**
* 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
}) })
} }
export default withData(config) function createIsomorphLink () {
if (typeof window === 'undefined') {
const { SchemaLink } = require('apollo-link-schema')
const { schema } = require('../graphql')
return new SchemaLink({ schema })
} else {
const { HttpLink } = require('apollo-link-http')
return new HttpLink({
uri: '/api/graphql',
credentials: 'same-origin'
})
}
}

View File

@ -9,8 +9,5 @@ module.exports = withSass({
config.optimization.minimizer.push(new OptimizeCSSAssetsPlugin({})) config.optimization.minimizer.push(new OptimizeCSSAssetsPlugin({}))
return config return config
},
env: {
GRAPHQL_API_URI: 'http://localhost:3000/api/graphql'
} }
}) })

38
package-lock.json generated
View File

@ -1911,6 +1911,15 @@
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"apollo-link-schema": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/apollo-link-schema/-/apollo-link-schema-1.2.4.tgz",
"integrity": "sha512-tLpqd2PSddXJDqA25aERh1U6SsZZBX1isPXSa8FbxvU4Dd8n/DmCOE4mqHyFasgjqLN1zFWL7jgf964LPrAp7A==",
"requires": {
"apollo-link": "^1.2.13",
"tslib": "^1.9.3"
}
},
"apollo-server-caching": { "apollo-server-caching": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.1.tgz", "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.1.tgz",
@ -6932,15 +6941,6 @@
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
}, },
"isomorphic-unfetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.0.0.tgz",
"integrity": "sha512-V0tmJSYfkKokZ5mgl0cmfQMTb7MLHsBMngTkbLY0eXvKqiVRRoZP04Ly+KhKrJfKtzC9E6Pp15Jo+bwh7Vi2XQ==",
"requires": {
"node-fetch": "^2.2.0",
"unfetch": "^4.0.0"
}
},
"isstream": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@ -8214,31 +8214,11 @@
} }
} }
}, },
"next-apollo": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/next-apollo/-/next-apollo-3.1.10.tgz",
"integrity": "sha512-VX64IW7h6Rdg+Ma0y8ryAIz7IfyLaz6Nd4VITuhfXFZ3nj51blcjRR6kc4E945SPk2WdrIMlLRZubgPRAgh9Ug==",
"requires": {
"@babel/runtime": "^7.4.5",
"isomorphic-unfetch": "^3.0.0",
"prop-types": "15.7.2",
"prop-types-exact": "1.2.0",
"url": "0.11.0"
}
},
"next-tick": { "next-tick": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
}, },
"next-with-apollo": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/next-with-apollo/-/next-with-apollo-5.0.0.tgz",
"integrity": "sha512-JcMecKv6XtzsLf1hPKrbxM6g9sygnGdQugglmCDkfWhHZt1yziPa+g515qtw7in8A7Ed5W9PdaaaUn7CWWQ71w==",
"requires": {
"isomorphic-unfetch": "^3.0.0"
}
},
"nice-try": { "nice-try": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",

View File

@ -12,12 +12,11 @@
"@apollo/react-ssr": "^3.1.3", "@apollo/react-ssr": "^3.1.3",
"@zeit/next-sass": "^1.0.1", "@zeit/next-sass": "^1.0.1",
"apollo-boost": "^0.4.7", "apollo-boost": "^0.4.7",
"apollo-link-schema": "^1.2.4",
"apollo-server-micro": "^2.10.1", "apollo-server-micro": "^2.10.1",
"graphql-tag": "^2.10.3", "graphql-tag": "^2.10.3",
"isomorphic-unfetch": "^3.0.0", "graphql-tools": "^4.0.7",
"next": "9.2.2", "next": "9.2.2",
"next-apollo": "^3.1.10",
"next-with-apollo": "^5.0.0",
"node-sass": "^4.13.1", "node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.3", "optimize-css-assets-webpack-plugin": "^5.0.3",
"react": "16.13.0", "react": "16.13.0",

View File

@ -1,9 +1,7 @@
const { ApolloServer, gql } = require('apollo-server-micro') import { ApolloServer } from 'apollo-server-micro'
const { typeDefs } = require('../../graphql/typeDefs') import { schema } from '../../graphql'
const { resolvers } = require('../../graphql/resolvers')
const apolloServer = new ApolloServer({ typeDefs, resolvers }) const apolloServer = new ApolloServer({ schema })
const handler = apolloServer.createHandler({ path: '/api/graphql' })
export const config = { api: { bodyParser: false } } export const config = { api: { bodyParser: false } }
export default handler export default apolloServer.createHandler({ path: '/api/graphql' })

View File

@ -2,7 +2,7 @@ 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 withData from '../lib/apollo' import { withApollo } from '../lib/apollo'
const Articles = () => { const Articles = () => {
return ( return (
@ -31,4 +31,4 @@ const Articles = () => {
) )
} }
export default withData(Articles) export default withApollo(Articles)