diff --git a/apollo/queries/articles/articles.js b/apollo/queries/articles/articles.js new file mode 100644 index 0000000..69bce61 --- /dev/null +++ b/apollo/queries/articles/articles.js @@ -0,0 +1,12 @@ +import gql from 'graphql-tag' + +const ARTICLES_QUERY = gql` + query Articles { + articles { + id + title + } + } +`; + +export default ARTICLES_QUERY \ No newline at end of file diff --git a/components/Header/Header.js b/components/Header/Header.js index 7c07983..aab3b56 100644 --- a/components/Header/Header.js +++ b/components/Header/Header.js @@ -14,6 +14,11 @@ const Header = () => ( About +
  • + + Articles + +
  • ) diff --git a/components/Query/query.js b/components/Query/query.js new file mode 100644 index 0000000..1b357be --- /dev/null +++ b/components/Query/query.js @@ -0,0 +1,17 @@ +import { useQuery } from '@apollo/react-hooks' + +const Query = ({ children, query, id }) => { + const { data, loading, error } = useQuery(query, { + variables: {id} + }) + + if (loading) return ( +
    +

    loading...

    +
    + ) + if (error) return

    Error: {JSON.stringify(error)}

    + return children({ data }) +} + +export default Query \ No newline at end of file diff --git a/lib/apollo.js b/lib/apollo.js new file mode 100644 index 0000000..d825efe --- /dev/null +++ b/lib/apollo.js @@ -0,0 +1,153 @@ +// import withApollo from 'next-with-apollo'; +// import ApolloClient, { InMemoryCache } from 'apollo-boost'; +// import { ApolloProvider } from '@apollo/react-hooks'; + +// export default withApollo( +// ({ initialState }) => { +// return new ApolloClient({ +// uri: 'http://localhost:3005/graphql', +// cache: new InMemoryCache().restore(initialState || {}) +// }); +// }, +// { +// render: ({ Page, props }) => { +// return ( +// +// +// +// ); +// } +// } +// ); +import React, { useMemo } from 'react' +import Head from 'next/head' +import { ApolloProvider } from '@apollo/react-hooks' +import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost' +import fetch from 'isomorphic-unfetch' + +const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:3005/graphql' + + let apolloClient = null + + /** + * Creates and provides the apolloContext + * 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 = useMemo( + () => apolloClient || initApolloClient(apolloState), + [] + ) + return ( + + + + ) + } + + // 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})` + } + + // Allow Next.js to remove getInitialProps from the browser build + if (typeof window === 'undefined') { + if (ssr) { + WithApollo.getInitialProps = async ctx => { + const { AppTree } = ctx + + let pageProps = {} + if (PageComponent.getInitialProps) { + pageProps = await PageComponent.getInitialProps(ctx) + } + + // Run all GraphQL queries in the component tree + // and extract the resulting data + const apolloClient = initApolloClient() + + try { + // Run all GraphQL queries + await require('@apollo/react-ssr').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() + + // 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 (!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) + }) + } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3688221..851becd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,36 @@ "cross-fetch": "3.0.4" } }, + "@apollo/react-common": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@apollo/react-common/-/react-common-3.1.3.tgz", + "integrity": "sha512-Q7ZjDOeqjJf/AOGxUMdGxKF+JVClRXrYBGVq+SuVFqANRpd68MxtVV2OjCWavsFAN0eqYnRqRUrl7vtUCiJqeg==", + "requires": { + "ts-invariant": "^0.4.4", + "tslib": "^1.10.0" + } + }, + "@apollo/react-hooks": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-3.1.3.tgz", + "integrity": "sha512-reIRO9xKdfi+B4gT/o/hnXuopUnm7WED/ru8VQydPw+C/KG/05Ssg1ZdxFKHa3oxwiTUIDnevtccIH35POanbA==", + "requires": { + "@apollo/react-common": "^3.1.3", + "@wry/equality": "^0.1.9", + "ts-invariant": "^0.4.4", + "tslib": "^1.10.0" + } + }, + "@apollo/react-ssr": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.3.tgz", + "integrity": "sha512-fUTmEYHxSTX1GA43B8vICxXXplpcEBnDwn0IgdAc3eG0p2YK97ZrJDRFCJ5vD7fyDZsrYhMf+rAI3sd+H2SS+A==", + "requires": { + "@apollo/react-common": "^3.1.3", + "@apollo/react-hooks": "^3.1.3", + "tslib": "^1.10.0" + } + }, "@babel/code-frame": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", @@ -1022,11 +1052,21 @@ "resolved": "https://registry.npmjs.org/@next/polyfill-nomodule/-/polyfill-nomodule-9.2.2.tgz", "integrity": "sha512-J51QpynN0AxQ0saWkxgpr0JxWqSJFCBOOeQpbyn5hlgxjrcXR9dQ59tN677vbq2wJ8idLn898vZFaRmQsLq13A==" }, + "@types/node": { + "version": "13.7.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz", + "integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg==" + }, "@types/q": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" }, + "@types/zen-observable": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", + "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -1185,6 +1225,23 @@ "@xtuc/long": "4.2.2" } }, + "@wry/context": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.4.4.tgz", + "integrity": "sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag==", + "requires": { + "@types/node": ">=6", + "tslib": "^1.9.3" + } + }, + "@wry/equality": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.1.9.tgz", + "integrity": "sha512-mB6ceGjpMGz1ZTza8HYnrPGos2mC6So4NhS1PtZ8s4Qt0K7fBiIGhpSxUbQmhwcSWE3no+bYxmI2OL6KuXYmoQ==", + "requires": { + "tslib": "^1.9.3" + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -1557,6 +1614,110 @@ "picomatch": "^2.0.4" } }, + "apollo-boost": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.4.7.tgz", + "integrity": "sha512-jfc3aqO0vpCV+W662EOG5gq4AH94yIsvSgAUuDvS3o/Z+8Joqn4zGC9CgLCDHusK30mFgtsEgwEe0pZoedohsQ==", + "requires": { + "apollo-cache": "^1.3.4", + "apollo-cache-inmemory": "^1.6.5", + "apollo-client": "^2.6.7", + "apollo-link": "^1.0.6", + "apollo-link-error": "^1.0.3", + "apollo-link-http": "^1.3.1", + "graphql-tag": "^2.4.2", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0" + } + }, + "apollo-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.4.tgz", + "integrity": "sha512-7X5aGbqaOWYG+SSkCzJNHTz2ZKDcyRwtmvW4mGVLRqdQs+HxfXS4dUS2CcwrAj449se6tZ6NLUMnjko4KMt3KA==", + "requires": { + "apollo-utilities": "^1.3.3", + "tslib": "^1.10.0" + } + }, + "apollo-cache-inmemory": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.5.tgz", + "integrity": "sha512-koB76JUDJaycfejHmrXBbWIN9pRKM0Z9CJGQcBzIOtmte1JhEBSuzsOUu7NQgiXKYI4iGoMREcnaWffsosZynA==", + "requires": { + "apollo-cache": "^1.3.4", + "apollo-utilities": "^1.3.3", + "optimism": "^0.10.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0" + } + }, + "apollo-client": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.8.tgz", + "integrity": "sha512-0zvJtAcONiozpa5z5zgou83iEKkBaXhhSSXJebFHRXs100SecDojyUWKjwTtBPn9HbM6o5xrvC5mo9VQ5fgAjw==", + "requires": { + "@types/zen-observable": "^0.8.0", + "apollo-cache": "1.3.4", + "apollo-link": "^1.0.0", + "apollo-utilities": "1.3.3", + "symbol-observable": "^1.0.2", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0", + "zen-observable": "^0.8.0" + } + }, + "apollo-link": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.13.tgz", + "integrity": "sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw==", + "requires": { + "apollo-utilities": "^1.3.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable-ts": "^0.8.20" + } + }, + "apollo-link-error": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.12.tgz", + "integrity": "sha512-psNmHyuy3valGikt/XHJfe0pKJnRX19tLLs6P6EHRxg+6q6JMXNVLYPaQBkL0FkwdTCB0cbFJAGRYCBviG8TDA==", + "requires": { + "apollo-link": "^1.2.13", + "apollo-link-http-common": "^0.2.15", + "tslib": "^1.9.3" + } + }, + "apollo-link-http": { + "version": "1.5.16", + "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.16.tgz", + "integrity": "sha512-IA3xA/OcrOzINRZEECI6IdhRp/Twom5X5L9jMehfzEo2AXdeRwAMlH5LuvTZHgKD8V1MBnXdM6YXawXkTDSmJw==", + "requires": { + "apollo-link": "^1.2.13", + "apollo-link-http-common": "^0.2.15", + "tslib": "^1.9.3" + } + }, + "apollo-link-http-common": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.15.tgz", + "integrity": "sha512-+Heey4S2IPsPyTf8Ag3PugUupASJMW894iVps6hXbvwtg1aHSNMXUYO5VG7iRHkPzqpuzT4HMBanCTXPjtGzxg==", + "requires": { + "apollo-link": "^1.2.13", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "apollo-utilities": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.3.tgz", + "integrity": "sha512-F14aX2R/fKNYMvhuP2t9GD9fggID7zp5I96MF5QeKYWDWTrkRdHRp4+SVfXUVN+cXOaB/IebfvRtzPf25CM0zw==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -4043,6 +4204,11 @@ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, + "graphql-tag": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz", + "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==" + }, "gzip-size": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", @@ -4628,6 +4794,15 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "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": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -5486,6 +5661,14 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "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" + } + }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", @@ -5828,6 +6011,14 @@ "mimic-fn": "^1.0.0" } }, + "optimism": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.10.3.tgz", + "integrity": "sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw==", + "requires": { + "@wry/context": "^0.4.0" + } + }, "optimize-css-assets-webpack-plugin": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", @@ -8325,6 +8516,11 @@ "util.promisify": "~1.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -8571,6 +8767,14 @@ "glob": "^7.1.2" } }, + "ts-invariant": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz", + "integrity": "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==", + "requires": { + "tslib": "^1.9.3" + } + }, "ts-pnp": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz", @@ -9732,6 +9936,20 @@ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" } } + }, + "zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "zen-observable-ts": { + "version": "0.8.20", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz", + "integrity": "sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA==", + "requires": { + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } } } } diff --git a/package.json b/package.json index 9da2e0a..ec5ae28 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,14 @@ "start": "next start" }, "dependencies": { + "@apollo/react-hooks": "^3.1.3", + "@apollo/react-ssr": "^3.1.3", "@zeit/next-sass": "^1.0.1", + "apollo-boost": "^0.4.7", + "graphql-tag": "^2.10.3", + "isomorphic-unfetch": "^3.0.0", "next": "9.2.2", + "next-with-apollo": "^5.0.0", "node-sass": "^4.13.1", "optimize-css-assets-webpack-plugin": "^5.0.3", "react": "16.13.0", diff --git a/pages/articles.js b/pages/articles.js new file mode 100644 index 0000000..92f5543 --- /dev/null +++ b/pages/articles.js @@ -0,0 +1,36 @@ +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'; + + +const Articles = () => { + return ( + <> + + +

    Articles page

    +
    + + {({ data: { articles } }) => { + return articles.map(art => { + return ( +
    +

    + title: {art.title}
    + id: {art.id} +

    +
    + ) + }) + }} +
    +
    +
    + + ) + +} + +export default withApollo(Articles)