mirror of
https://github.com/sergeyyarkov/next-apollo-graphql-setup.git
synced 2026-10-11 02:09:23 +03:00
restore
This commit is contained in:
commit
ca9c6e2aec
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/node_modules
|
||||||
|
|
||||||
|
/.next/
|
||||||
|
|
||||||
|
/build
|
||||||
|
|
||||||
|
npm-debug.log
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
217
.eslintrc.json
Normal file
217
.eslintrc.json
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
{
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"plugins": [
|
||||||
|
"import",
|
||||||
|
"node",
|
||||||
|
"promise",
|
||||||
|
"standard",
|
||||||
|
"jsx-a11y"
|
||||||
|
],
|
||||||
|
|
||||||
|
"globals": {
|
||||||
|
"document": "readonly",
|
||||||
|
"navigator": "readonly",
|
||||||
|
"window": "readonly"
|
||||||
|
},
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
"accessor-pairs": "error",
|
||||||
|
"array-bracket-spacing": ["error", "never"],
|
||||||
|
"arrow-spacing": ["error", { "before": true, "after": true }],
|
||||||
|
"block-spacing": ["error", "always"],
|
||||||
|
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
||||||
|
"camelcase": ["error", { "properties": "never" }],
|
||||||
|
"comma-dangle": ["error", {
|
||||||
|
"arrays": "never",
|
||||||
|
"objects": "never",
|
||||||
|
"imports": "never",
|
||||||
|
"exports": "never",
|
||||||
|
"functions": "never"
|
||||||
|
}],
|
||||||
|
"comma-spacing": ["error", { "before": false, "after": true }],
|
||||||
|
"comma-style": ["error", "last"],
|
||||||
|
"computed-property-spacing": ["error", "never"],
|
||||||
|
"constructor-super": "error",
|
||||||
|
"curly": ["error", "multi-line"],
|
||||||
|
"dot-location": ["error", "property"],
|
||||||
|
"dot-notation": ["error", { "allowKeywords": true }],
|
||||||
|
"eol-last": "error",
|
||||||
|
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||||
|
"func-call-spacing": ["error", "never"],
|
||||||
|
"generator-star-spacing": ["error", { "before": true, "after": true }],
|
||||||
|
"handle-callback-err": ["error", "^(err|error)$" ],
|
||||||
|
"indent": ["error", 2, {
|
||||||
|
"SwitchCase": 1,
|
||||||
|
"VariableDeclarator": 1,
|
||||||
|
"outerIIFEBody": 1,
|
||||||
|
"MemberExpression": 1,
|
||||||
|
"FunctionDeclaration": { "parameters": 1, "body": 1 },
|
||||||
|
"FunctionExpression": { "parameters": 1, "body": 1 },
|
||||||
|
"CallExpression": { "arguments": 1 },
|
||||||
|
"ArrayExpression": 1,
|
||||||
|
"ObjectExpression": 1,
|
||||||
|
"ImportDeclaration": 1,
|
||||||
|
"flatTernaryExpressions": false,
|
||||||
|
"ignoreComments": false,
|
||||||
|
"ignoredNodes": ["TemplateLiteral *"]
|
||||||
|
}],
|
||||||
|
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
|
||||||
|
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||||
|
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
|
||||||
|
"new-cap": ["error", { "newIsCap": true, "capIsNew": false, "properties": true }],
|
||||||
|
"new-parens": "error",
|
||||||
|
"no-array-constructor": "error",
|
||||||
|
"no-async-promise-executor": "error",
|
||||||
|
"no-caller": "error",
|
||||||
|
"no-case-declarations": "error",
|
||||||
|
"no-class-assign": "error",
|
||||||
|
"no-compare-neg-zero": "error",
|
||||||
|
"no-cond-assign": "error",
|
||||||
|
"no-const-assign": "error",
|
||||||
|
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||||
|
"no-control-regex": "error",
|
||||||
|
"no-debugger": "error",
|
||||||
|
"no-delete-var": "error",
|
||||||
|
"no-dupe-args": "error",
|
||||||
|
"no-dupe-class-members": "error",
|
||||||
|
"no-dupe-keys": "error",
|
||||||
|
"no-duplicate-case": "error",
|
||||||
|
"no-empty-character-class": "error",
|
||||||
|
"no-empty-pattern": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-ex-assign": "error",
|
||||||
|
"no-extend-native": "error",
|
||||||
|
"no-extra-bind": "error",
|
||||||
|
"no-extra-boolean-cast": "error",
|
||||||
|
"no-extra-parens": ["error", "functions"],
|
||||||
|
"no-fallthrough": "error",
|
||||||
|
"no-floating-decimal": "error",
|
||||||
|
"no-func-assign": "error",
|
||||||
|
"no-global-assign": "error",
|
||||||
|
"no-implied-eval": "error",
|
||||||
|
"no-inner-declarations": ["error", "functions"],
|
||||||
|
"no-invalid-regexp": "error",
|
||||||
|
"no-irregular-whitespace": "error",
|
||||||
|
"no-iterator": "error",
|
||||||
|
"no-labels": ["error", { "allowLoop": false, "allowSwitch": false }],
|
||||||
|
"no-lone-blocks": "error",
|
||||||
|
"no-misleading-character-class": "error",
|
||||||
|
"no-prototype-builtins": "error",
|
||||||
|
"no-useless-catch": "error",
|
||||||
|
"no-mixed-operators": ["error", {
|
||||||
|
"groups": [
|
||||||
|
["==", "!=", "===", "!==", ">", ">=", "<", "<="],
|
||||||
|
["&&", "||"],
|
||||||
|
["in", "instanceof"]
|
||||||
|
],
|
||||||
|
"allowSamePrecedence": true
|
||||||
|
}],
|
||||||
|
"no-mixed-spaces-and-tabs": "error",
|
||||||
|
"no-multi-spaces": "error",
|
||||||
|
"no-multi-str": "error",
|
||||||
|
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }],
|
||||||
|
"no-negated-in-lhs": "error",
|
||||||
|
"no-new": "error",
|
||||||
|
"no-new-func": "error",
|
||||||
|
"no-new-object": "error",
|
||||||
|
"no-new-require": "error",
|
||||||
|
"no-new-symbol": "error",
|
||||||
|
"no-new-wrappers": "error",
|
||||||
|
"no-obj-calls": "error",
|
||||||
|
"no-octal": "error",
|
||||||
|
"no-octal-escape": "error",
|
||||||
|
"no-path-concat": "error",
|
||||||
|
"no-proto": "error",
|
||||||
|
"no-redeclare": ["error", { "builtinGlobals": false }],
|
||||||
|
"no-regex-spaces": "error",
|
||||||
|
"no-return-assign": ["error", "except-parens"],
|
||||||
|
"no-return-await": "error",
|
||||||
|
"no-self-assign": ["error", { "props": true }],
|
||||||
|
"no-self-compare": "error",
|
||||||
|
"no-sequences": "error",
|
||||||
|
"no-shadow-restricted-names": "error",
|
||||||
|
"no-sparse-arrays": "error",
|
||||||
|
"no-tabs": "error",
|
||||||
|
"no-template-curly-in-string": "error",
|
||||||
|
"no-this-before-super": "error",
|
||||||
|
"no-throw-literal": "error",
|
||||||
|
"no-trailing-spaces": "error",
|
||||||
|
"no-undef": "error",
|
||||||
|
"no-undef-init": "error",
|
||||||
|
"no-unexpected-multiline": "error",
|
||||||
|
"no-unmodified-loop-condition": "error",
|
||||||
|
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
||||||
|
"no-unreachable": "error",
|
||||||
|
"no-unsafe-finally": "error",
|
||||||
|
"no-unsafe-negation": "error",
|
||||||
|
"no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }],
|
||||||
|
"no-unused-vars": ["off", { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
|
||||||
|
"no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }],
|
||||||
|
"no-useless-call": "error",
|
||||||
|
"no-useless-computed-key": "error",
|
||||||
|
"no-useless-constructor": "error",
|
||||||
|
"no-useless-escape": "error",
|
||||||
|
"no-useless-rename": "error",
|
||||||
|
"no-useless-return": "error",
|
||||||
|
"no-void": "error",
|
||||||
|
"no-whitespace-before-property": "error",
|
||||||
|
"no-with": "error",
|
||||||
|
"object-curly-newline": ["error", { "multiline": true, "consistent": true }],
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }],
|
||||||
|
"one-var": ["error", { "initialized": "never" }],
|
||||||
|
"operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before", "|>": "before" } }],
|
||||||
|
"padded-blocks": ["error", { "blocks": "never", "switches": "never", "classes": "never" }],
|
||||||
|
"prefer-const": ["error", {"destructuring": "all"}],
|
||||||
|
"prefer-promise-reject-errors": "error",
|
||||||
|
"quote-props": ["error", "as-needed"],
|
||||||
|
"quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": false }],
|
||||||
|
"rest-spread-spacing": ["error", "never"],
|
||||||
|
"semi": ["error", "never"],
|
||||||
|
"semi-spacing": ["error", { "before": false, "after": true }],
|
||||||
|
"space-before-blocks": ["error", "always"],
|
||||||
|
"space-before-function-paren": ["error", "always"],
|
||||||
|
"space-in-parens": ["error", "never"],
|
||||||
|
"space-infix-ops": "error",
|
||||||
|
"space-unary-ops": ["error", { "words": true, "nonwords": false }],
|
||||||
|
"spaced-comment": ["error", "always", {
|
||||||
|
"line": { "markers": ["*package", "!", "/", ",", "="] },
|
||||||
|
"block": { "balanced": true, "markers": ["*package", "!", ",", ":", "::", "flow-include"], "exceptions": ["*"] }
|
||||||
|
}],
|
||||||
|
"symbol-description": "error",
|
||||||
|
"template-curly-spacing": ["error", "never"],
|
||||||
|
"template-tag-spacing": ["error", "never"],
|
||||||
|
"unicode-bom": ["error", "never"],
|
||||||
|
"use-isnan": "error",
|
||||||
|
"valid-typeof": ["error", { "requireStringLiterals": true }],
|
||||||
|
"wrap-iife": ["error", "any", { "functionPrototypeMethods": true }],
|
||||||
|
"yield-star-spacing": ["error", "both"],
|
||||||
|
"yoda": ["error", "never"],
|
||||||
|
|
||||||
|
"import/export": "error",
|
||||||
|
"import/first": "error",
|
||||||
|
"import/no-absolute-path": ["error", { "esmodule": true, "commonjs": true, "amd": false }],
|
||||||
|
"import/no-duplicates": "error",
|
||||||
|
"import/no-named-default": "error",
|
||||||
|
"import/no-webpack-loader-syntax": "error",
|
||||||
|
|
||||||
|
"node/no-deprecated-api": "error",
|
||||||
|
"node/process-exit-as-throw": "error",
|
||||||
|
|
||||||
|
"promise/param-names": "error",
|
||||||
|
|
||||||
|
"standard/no-callback-literal": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
FROM node:10
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Sergey Yarkov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
75
README.md
Normal file
75
README.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
### This is a next-apollo-graphql setup to start develop your web site. This setup uses server side rendering.
|
||||||
|
|
||||||
|
|
||||||
|
## 🚀 Getting Started
|
||||||
|
|
||||||
|
To start the development server, you need to run the following commands below.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
Open http://localhost:3000 with your browser to see the result.
|
||||||
|
|
||||||
|
Our application runs on port `3000`, and GraphQL server also runs on `localhost:3000/api/graphql`.
|
||||||
|
|
||||||
|
## ⚡ Using Query
|
||||||
|
|
||||||
|
You can easily use `Query` component to send queries to the GraphQL server. Just pass your query to the `Query` component.
|
||||||
|
```jsx
|
||||||
|
<Query query={ARTICLES_QUERY}>
|
||||||
|
{({ data: { articles } }) => {
|
||||||
|
return <Articles articles={articles} />;
|
||||||
|
}}
|
||||||
|
</Query>
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the `withData` HOC to wrap your pages.
|
||||||
|
```jsx
|
||||||
|
|
||||||
|
// ./frontend/pages/articles.js
|
||||||
|
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 withData 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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withData(Articles)
|
||||||
|
|
||||||
|
```
|
||||||
|
## License:
|
||||||
|
[MIT](https://github.com/sergeyyarkov/next-apollo-graphql-setup/blob/master/LICENSE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
12
apollo/queries/articles/articles.js
Normal file
12
apollo/queries/articles/articles.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
const ARTICLES_QUERY = gql`
|
||||||
|
query Articles {
|
||||||
|
articles {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default ARTICLES_QUERY
|
||||||
10
assets/css/global.scss
Normal file
10
assets/css/global.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Put some global styles in this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
& li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
components/Header/Header.js
Normal file
26
components/Header/Header.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Header
|
||||||
9
components/Header/Header.module.scss
Normal file
9
components/Header/Header.module.scss
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.Header {
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
display: flex;
|
||||||
|
& li {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
components/Layout/Layout.js
Normal file
14
components/Layout/Layout.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Header from '../Header/Header'
|
||||||
|
|
||||||
|
const Layout = props => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<main>
|
||||||
|
{props.children}
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Layout
|
||||||
19
components/Query/query.js
Normal file
19
components/Query/query.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
|
|
||||||
|
const Query = ({ children, query, id }) => {
|
||||||
|
const { data, loading, error } = useQuery(query, {
|
||||||
|
variables: { id }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="uk-container">
|
||||||
|
<p>loading...</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (error) return <p>Error: {JSON.stringify(error)}</p>
|
||||||
|
return children({ data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Query
|
||||||
21
components/SEO/SEO.js
Normal file
21
components/SEO/SEO.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
/*
|
||||||
|
Put some SEO tags in Head component.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SEO = props => {
|
||||||
|
const { pageTitle } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Head>
|
||||||
|
<title>{pageTitle}</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="-"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SEO
|
||||||
14
docker-compose.yml
Normal file
14
docker-compose.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
container_name: apollo-next-graphql
|
||||||
|
restart: always
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- '3000'
|
||||||
|
webserver:
|
||||||
|
container_name: webserver
|
||||||
|
restart: unless-stopped
|
||||||
|
build: nginx
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
11
graphql/data/index.js
Normal file
11
graphql/data/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
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' }
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
articles
|
||||||
|
}
|
||||||
8
graphql/index.js
Normal file
8
graphql/index.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { makeExecutableSchema } from 'graphql-tools'
|
||||||
|
import { typeDefs } from './typeDefs'
|
||||||
|
import { resolvers } from './resolvers'
|
||||||
|
|
||||||
|
export const schema = makeExecutableSchema({
|
||||||
|
typeDefs,
|
||||||
|
resolvers
|
||||||
|
})
|
||||||
11
graphql/models/Article.js
Normal file
11
graphql/models/Article.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const { articles } = require('../data')
|
||||||
|
|
||||||
|
class Article {
|
||||||
|
static all () {
|
||||||
|
return articles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Article
|
||||||
|
}
|
||||||
5
graphql/models/index.js
Normal file
5
graphql/models/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const { Article } = require('./Article')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Article
|
||||||
|
}
|
||||||
11
graphql/resolvers/articlesResolvers.js
Normal file
11
graphql/resolvers/articlesResolvers.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const { Article } = require('../models')
|
||||||
|
|
||||||
|
const articlesResolvers = {
|
||||||
|
Query: {
|
||||||
|
articles: () => Article.all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
articlesResolvers
|
||||||
|
}
|
||||||
7
graphql/resolvers/index.js
Normal file
7
graphql/resolvers/index.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const { articlesResolvers } = require('./articlesResolvers')
|
||||||
|
|
||||||
|
const resolvers = [articlesResolvers]
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
resolvers
|
||||||
|
}
|
||||||
8
graphql/typeDefs/index.js
Normal file
8
graphql/typeDefs/index.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const { query } = require('./query')
|
||||||
|
const { articleType } = require('./types')
|
||||||
|
|
||||||
|
const typeDefs = [query, articleType]
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
typeDefs
|
||||||
|
}
|
||||||
11
graphql/typeDefs/query.js
Normal file
11
graphql/typeDefs/query.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const { gql } = require('apollo-boost')
|
||||||
|
|
||||||
|
const query = gql`
|
||||||
|
type Query {
|
||||||
|
articles: [Article]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
query
|
||||||
|
}
|
||||||
12
graphql/typeDefs/types/articleType.js
Normal file
12
graphql/typeDefs/types/articleType.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const { gql } = require('apollo-boost')
|
||||||
|
|
||||||
|
const articleType = gql`
|
||||||
|
type Article {
|
||||||
|
id: ID
|
||||||
|
title: String
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
articleType
|
||||||
|
}
|
||||||
5
graphql/typeDefs/types/index.js
Normal file
5
graphql/typeDefs/types/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const { articleType } = require('./articleType')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
articleType
|
||||||
|
}
|
||||||
148
lib/apollo.js
Normal file
148
lib/apollo.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import { ApolloProvider } from '@apollo/react-hooks'
|
||||||
|
import { ApolloClient } from 'apollo-client'
|
||||||
|
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||||
|
|
||||||
|
let globalApolloClient = 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 = 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
13
next.config.js
Normal file
13
next.config.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const withSass = require('@zeit/next-sass')
|
||||||
|
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
|
||||||
|
|
||||||
|
module.exports = withSass({
|
||||||
|
cssModules: true,
|
||||||
|
webpack: (config, { dev, isServer }) => {
|
||||||
|
if (isServer) return config
|
||||||
|
|
||||||
|
config.optimization.minimizer.push(new OptimizeCSSAssetsPlugin({}))
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
})
|
||||||
5
nginx/Dockerfile
Normal file
5
nginx/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
FROM nginx
|
||||||
|
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
COPY default.conf /etc/nginx/conf.d/
|
||||||
20
nginx/default.conf
Normal file
20
nginx/default.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://apollo-next-graphql:3000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
location = /50x.html {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
15632
package-lock.json
generated
Normal file
15632
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
package.json
Normal file
36
package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "apollo-next-graphql-setup",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"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",
|
||||||
|
"apollo-link-schema": "^1.2.4",
|
||||||
|
"apollo-server-micro": "^2.10.1",
|
||||||
|
"graphql-tag": "^2.10.3",
|
||||||
|
"graphql-tools": "^4.0.7",
|
||||||
|
"next": "9.2.2",
|
||||||
|
"node-sass": "^4.13.1",
|
||||||
|
"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-jsx-a11y": "^6.2.3",
|
||||||
|
"eslint-plugin-node": "^11.0.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-react": "^7.18.3",
|
||||||
|
"eslint-plugin-standard": "^4.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
pages/_app.js
Normal file
5
pages/_app.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import '../assets/css/global.scss'
|
||||||
|
|
||||||
|
export default function Application ({ Component, pageProps }) {
|
||||||
|
return <Component {...pageProps} />
|
||||||
|
}
|
||||||
17
pages/_document.js
Normal file
17
pages/_document.js
Normal file
@ -0,0 +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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomDocument
|
||||||
13
pages/about.js
Normal file
13
pages/about.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Layout from '../components/Layout/Layout'
|
||||||
|
import SEO from '../components/SEO/SEO'
|
||||||
|
|
||||||
|
const About = () => (
|
||||||
|
<>
|
||||||
|
<SEO pageTitle='About page' />
|
||||||
|
<Layout>
|
||||||
|
<h1>About page</h1>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default About
|
||||||
7
pages/api/graphql.js
Normal file
7
pages/api/graphql.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { ApolloServer } from 'apollo-server-micro'
|
||||||
|
import { schema } from '../../graphql'
|
||||||
|
|
||||||
|
const apolloServer = new ApolloServer({ schema })
|
||||||
|
|
||||||
|
export const config = { api: { bodyParser: false } }
|
||||||
|
export default apolloServer.createHandler({ path: '/api/graphql' })
|
||||||
34
pages/articles.js
Normal file
34
pages/articles.js
Normal file
@ -0,0 +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'
|
||||||
|
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withApollo(Articles)
|
||||||
13
pages/index.js
Normal file
13
pages/index.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Layout from '../components/Layout/Layout'
|
||||||
|
import SEO from '../components/SEO/SEO'
|
||||||
|
|
||||||
|
const Index = () => (
|
||||||
|
<>
|
||||||
|
<SEO pageTitle='Index page' />
|
||||||
|
<Layout>
|
||||||
|
<h1>Index page</h1>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Index
|
||||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
10
public/zeit.svg
Normal file
10
public/zeit.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg width="82" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="url(#prefix__paint0_linear)" d="M9.018 0l9.019 16H0L9.018 0z"/>
|
||||||
|
<path fill="#333" fill-rule="evenodd" d="M51.634 12.028h-6.492V2.052h6.492v1.256H46.61v3.007h4.37V7.57h-4.37v3.202h5.024v1.255zm-14.063 0h-7.235v-1.096l5.342-7.624h-5.253V2.052h7.058v1.097l-5.342 7.623h5.43v1.256zm21.88 0h6.333v-1.256h-2.423V3.308h2.423V2.052h-6.332v1.256h2.441v7.465h-2.441v1.255zm18.22 0h-1.468v-8.72h-3.36V2.052h8.225v1.256H77.67v8.72z" clip-rule="evenodd"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="prefix__paint0_linear" x1="28.022" x2="16.189" y1="22.991" y2="8.569" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#fff"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 794 B |
Loading…
x
Reference in New Issue
Block a user