├── .gitignore
├── .prettierrc
├── LICENSE
├── README.MD
├── package.json
├── packages
├── client
│ ├── .babelrc
│ ├── .eslintrc
│ ├── jest.setup.js
│ ├── lib
│ │ ├── apollo.init.ts
│ │ └── withApollo.tsx
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ └── index.tsx
│ ├── styled.d.ts
│ ├── test
│ │ └── index.spec.tsx
│ └── tsconfig.json
└── server
│ ├── .eslintrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── nodemon-debug.json
│ ├── nodemon.json
│ ├── package.json
│ ├── src
│ ├── app.module.ts
│ ├── common
│ │ └── config
│ │ │ ├── config.module.ts
│ │ │ └── config.service.ts
│ ├── main.ts
│ ├── schema.gql
│ └── user
│ │ ├── user.entity.ts
│ │ ├── user.module.ts
│ │ ├── user.resolver.ts
│ │ └── user.service.ts
│ ├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
2 |
3 | # Created by https://www.gitignore.io/api/macos,visualstudiocode,node
4 | # Edit at https://www.gitignore.io/?templates=macos,visualstudiocode,node
5 |
6 | ### macOS ###
7 | # General
8 | .DS_Store
9 | .AppleDouble
10 | .LSOverride
11 |
12 | # Icon must end with two \r
13 | Icon
14 |
15 | # Thumbnails
16 | ._*
17 |
18 | # Files that might appear in the root of a volume
19 | .DocumentRevisions-V100
20 | .fseventsd
21 | .Spotlight-V100
22 | .TemporaryItems
23 | .Trashes
24 | .VolumeIcon.icns
25 | .com.apple.timemachine.donotpresent
26 |
27 | # Directories potentially created on remote AFP share
28 | .AppleDB
29 | .AppleDesktop
30 | Network Trash Folder
31 | Temporary Items
32 | .apdisk
33 |
34 | ### Node ###
35 | # Logs
36 | logs
37 | *.log
38 | npm-debug.log*
39 | yarn-debug.log*
40 | yarn-error.log*
41 | lerna-debug.log*
42 |
43 | # Diagnostic reports (https://nodejs.org/api/report.html)
44 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
45 |
46 | # Runtime data
47 | pids
48 | *.pid
49 | *.seed
50 | *.pid.lock
51 |
52 | # Directory for instrumented libs generated by jscoverage/JSCover
53 | lib-cov
54 |
55 | # Coverage directory used by tools like istanbul
56 | coverage
57 | *.lcov
58 |
59 | # nyc test coverage
60 | .nyc_output
61 |
62 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
63 | .grunt
64 |
65 | # Bower dependency directory (https://bower.io/)
66 | bower_components
67 |
68 | # node-waf configuration
69 | .lock-wscript
70 |
71 | # Compiled binary addons (https://nodejs.org/api/addons.html)
72 | build/Release
73 |
74 | # Dependency directories
75 | node_modules/
76 | jspm_packages/
77 |
78 | # TypeScript v1 declaration files
79 | typings/
80 |
81 | # TypeScript cache
82 | *.tsbuildinfo
83 |
84 | # Optional npm cache directory
85 | .npm
86 |
87 | # Optional eslint cache
88 | .eslintcache
89 |
90 | # Optional REPL history
91 | .node_repl_history
92 |
93 | # Output of 'npm pack'
94 | *.tgz
95 |
96 | # Yarn Integrity file
97 | .yarn-integrity
98 |
99 | # dotenv environment variables file
100 | .env
101 | .env.test
102 | .env.*
103 |
104 | # parcel-bundler cache (https://parceljs.org/)
105 | .cache
106 |
107 | # next.js build output
108 | .next
109 |
110 | # nuxt.js build output
111 | .nuxt
112 |
113 | # vuepress build output
114 | .vuepress/dist
115 |
116 | # Serverless directories
117 | .serverless/
118 |
119 | # FuseBox cache
120 | .fusebox/
121 |
122 | # DynamoDB Local files
123 | .dynamodb/
124 |
125 | ### VisualStudioCode ###
126 | .vscode/*
127 | !.vscode/settings.json
128 | !.vscode/tasks.json
129 | !.vscode/launch.json
130 | !.vscode/extensions.json
131 |
132 | ### VisualStudioCode Patch ###
133 | # Ignore all local history of files
134 | .history
135 |
136 | # End of https://www.gitignore.io/api/macos,visualstudiocode,node
137 |
138 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
139 |
140 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "trailingComma": "all",
4 | "singleQuote": true
5 | }
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Twetlix
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 |
2 | Nestjs React GraphQL Boilerplate
3 |
4 |
5 | 
6 | 
7 | 
8 | 
9 | 
10 | 
11 | 
12 |
13 | > A Fullstack Web App boilerplate based on these bad boys, **GraphQL** / **Postgresql** / **Nestjs** / **React** 😘
14 |
15 | ## Backend
16 |
17 | **Must Read (important)**
18 | The backend of the boilerplate is as said above based on Nestjs which is a really powerful backend framework and since this boilerplate is all about using Typescript on both sides of the application Nestjs was a no brainer, for the database you can use any kind of database you want as that it will all be managed using TypeOrm. This backend is also GraphQL specific meaning it has the full GraphQL support with Typescript classes based schema generation which makes your schema fully typed during development.
19 |
20 | ## Frontend
21 |
22 | **Must Read (important)**
23 | The frontent of the boilerplate is based on Reactjs and using server-side-rendering using Nextjs, for communications between the frontend and the GraphQL backend it is using react apollo. For styling the boilerplate uses styled-components in conjunction with react-bootstrap to offer a solid grid system to build your app with.
24 |
25 | ## Why use this
26 |
27 | This boilerplate is for someone that wants firstly using a typed language like Typescript on both sides of the app, and it also uses GraphQL which is another major benefit of this boilerplate, as for React it is the leading frontend framework/library and it is one of the best ways someone could ever use to develop the frontend, Nestjs also provides a solid ground to build your backend with since it is very organized in terms of how you build your backend with it.
28 |
29 | ## Note
30 |
31 | This boilerplate is **NOT** for a beginner developer as that it requires a general knowledge of GraphQL, TypeScript, Nestjs and GraphQL
32 |
33 | ## Installation
34 |
35 | To start using this boilerplate, clone the repo to your desired destination and install the dependencies.
36 |
37 | ```bash
38 | $ git clone https://github.com/twetlix/nestjs-react-graphql-boilerplate.git
39 |
40 | $ cd
41 | $ yarn install
42 | ```
43 |
44 | ## Usage
45 |
46 | Run the following command to start the project in an auto-reloading development mode
47 |
48 | ```bash
49 | $ yarn dev
50 | ```
51 |
52 | And to build your project
53 |
54 | ```bash
55 | $ yarn build
56 | ```
57 |
58 | To run tests
59 |
60 | ```bash
61 | $ yarn test
62 | ```
63 |
64 | To lint the project
65 |
66 | ```bash
67 | $ yarn lint
68 | ```
69 |
70 | To format the project
71 |
72 | ```bash
73 | $ yarn format
74 | ```
75 |
76 | **There are other commands you can run for the boilerplate, just take a look at the package.json file in the root folder**
77 |
78 | ## Moreover
79 |
80 | One really great feature about this boilerplate is that every side of the application is separate meaning in the future the boilerplate is capable of expanding to other sides for example if you want a react-native app, just create it inside the packages folder and it will be a yarn workspace in the project!
81 |
82 | ## License
83 |
84 | MIT License
85 |
86 | Copyright (c) 2019 Twetlix
87 |
88 | Permission is hereby granted, free of charge, to any person obtaining a copy
89 | of this software and associated documentation files (the "Software"), to deal
90 | in the Software without restriction, including without limitation the rights
91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
92 | copies of the Software, and to permit persons to whom the Software is
93 | furnished to do so, subject to the following conditions:
94 |
95 | The above copyright notice and this permission notice shall be included in all
96 | copies or substantial portions of the Software.
97 |
98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
99 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
100 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
101 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
102 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
103 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
104 | SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "workspaces": [
4 | "packages/*"
5 | ],
6 | "devDependencies": {
7 | "concurrently": "^4.1.1"
8 | },
9 | "scripts": {
10 | "server:dev": "yarn workspace @nestjs-react-graphql-boilerplate/server dev",
11 | "client:dev": "yarn workspace @nestjs-react-graphql-boilerplate/client dev",
12 | "dev": "concurrently \"yarn server:dev\" \"yarn client:dev\"",
13 | "server:build": "yarn workspace @nestjs-react-graphql-boilerplate/server build",
14 | "client:build": "yarn workspace @nestjs-react-graphql-boilerplate/client build",
15 | "build": "concurrently \"yarn server:build\" \"yarn client:build\"",
16 | "server:start": "yarn workspace @nestjs-react-graphql-boilerplate/server start",
17 | "client:start": "yarn workspace @nestjs-react-graphql-boilerplate/client start",
18 | "start": "concurrently \"yarn server:start\" \"yarn client:start\"",
19 | "server:lint": "yarn workspace @nestjs-react-graphql-boilerplate/server lint",
20 | "client:lint": "yarn workspace @nestjs-react-graphql-boilerplate/client lint",
21 | "lint": "concurrently \"yarn server:lint\" \"yarn client:lint\"",
22 | "server:format": "yarn workspace @nestjs-react-graphql-boilerplate/server format",
23 | "client:format": "yarn workspace @nestjs-react-graphql-boilerplate/client format",
24 | "format": "concurrently \"yarn server:format\" \"yarn client:format\"",
25 | "server:test": "yarn workspace @nestjs-react-graphql-boilerplate/server test",
26 | "client:test": "yarn workspace @nestjs-react-graphql-boilerplate/client test",
27 | "test": "concurrently \"yarn server:test\" \"yarn client:test\"",
28 | "server:test:watch": "yarn workspace @nestjs-react-graphql-boilerplate/server test:watch",
29 | "client:test:watch": "yarn workspace @nestjs-react-graphql-boilerplate/client test:watch",
30 | "test:watch": "concurrently \"yarn server:test:watch\" \"yarn client:test:watch\"",
31 | "server:test:cov": "yarn workspace @nestjs-react-graphql-boilerplate/server test:cov",
32 | "client:test:cov": "yarn workspace @nestjs-react-graphql-boilerplate/client test:cov",
33 | "test:cov": "concurrently \"yarn server:test:cov\" \"yarn client:test:cov\""
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["next/babel"],
3 | "plugins": [["styled-components", { "ssr": true }]]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/client/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "globals": {
7 | "Atomics": "readonly",
8 | "SharedArrayBuffer": "readonly"
9 | },
10 | "parserOptions": {
11 | "ecmaFeatures": {
12 | "jsx": true
13 | },
14 | "ecmaVersion": 2018,
15 | "sourceType": "module"
16 | },
17 | "plugins": ["react"],
18 | "extends": [
19 | "plugin:@typescript-eslint/recommended",
20 | "prettier/@typescript-eslint",
21 | "react-app",
22 | "plugin:prettier/recommended"
23 | ],
24 | "rules": {
25 | "@typescript-eslint/indent": "off",
26 | "@typescript-eslint/no-parameter-properties": "off",
27 | "@typescript-eslint/explicit-member-accessibility": "off",
28 | "@typescript-eslint/explicit-function-return-type": "off",
29 | "@typescript-eslint/no-explicit-any": "off"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/client/jest.setup.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | const Enzyme = require('enzyme')
3 | const Adapter = require('enzyme-adapter-react-16')
4 |
5 | Enzyme.configure({ adapter: new Adapter() })
6 |
--------------------------------------------------------------------------------
/packages/client/lib/apollo.init.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ApolloClient,
3 | HttpLink,
4 | InMemoryCache,
5 | NormalizedCacheObject,
6 | } from 'apollo-boost'
7 | import fetch from 'isomorphic-unfetch'
8 |
9 | let apolloClient: ApolloClient | null = null
10 |
11 | function create(initialState?: any) {
12 | const isBrowser = typeof window !== 'undefined'
13 | return new ApolloClient({
14 | connectToDevTools: isBrowser,
15 | ssrMode: !isBrowser,
16 | link: new HttpLink({
17 | uri: 'http://localhost:5000/graphql',
18 | credentials: 'include',
19 | fetch: !isBrowser ? fetch : undefined,
20 | }),
21 | cache: new InMemoryCache().restore(initialState || {}),
22 | })
23 | }
24 |
25 | export default function initApollo(initialState?: any) {
26 | if (typeof window === 'undefined') {
27 | return create(initialState)
28 | }
29 |
30 | if (!apolloClient) {
31 | apolloClient = create(initialState)
32 | }
33 |
34 | return apolloClient
35 | }
36 |
--------------------------------------------------------------------------------
/packages/client/lib/withApollo.tsx:
--------------------------------------------------------------------------------
1 | import { ApolloClient, NormalizedCacheObject } from 'apollo-boost'
2 | import { NextPageContext } from 'next'
3 | import Head from 'next/head'
4 | import { SingletonRouter } from 'next/router'
5 | import React from 'react'
6 | import { getDataFromTree } from 'react-apollo'
7 | import initApollo from './apollo.init'
8 |
9 | interface PageContext extends NextPageContext {
10 | Component: React.ReactNode
11 | router: SingletonRouter
12 | }
13 |
14 | export default (App: any) => {
15 | return class Apollo extends React.Component {
16 | apolloClient: ApolloClient
17 | static displayName = 'withApollo(App)'
18 | static async getInitialProps(ctx: PageContext) {
19 | const { Component, router } = ctx
20 |
21 | let appProps = {}
22 | if (App.getInitialProps) {
23 | appProps = await App.getInitialProps(ctx)
24 | }
25 |
26 | const apollo = initApollo()
27 | if (typeof window === 'undefined') {
28 | try {
29 | await getDataFromTree(
30 | ,
36 | )
37 | } catch (error) {
38 | console.error('Error while running `getDataFromTree`', error)
39 | }
40 | Head.rewind()
41 | }
42 |
43 | const apolloState = apollo.cache.extract()
44 |
45 | return {
46 | ...appProps,
47 | apolloState,
48 | }
49 | }
50 |
51 | constructor(props: { apolloState: any }) {
52 | super(props)
53 | this.apolloClient = initApollo(props.apolloState)
54 | }
55 |
56 | render() {
57 | return
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/client/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/packages/client/next.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | const withCSS = require('@zeit/next-css')
3 |
4 | module.exports = withCSS()
5 |
--------------------------------------------------------------------------------
/packages/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@nestjs-react-graphql-boilerplate/client",
3 | "version": "0.0.1",
4 | "license": "MIT",
5 | "scripts": {
6 | "dev": "next",
7 | "start": "next start",
8 | "build": "next build",
9 | "lint": "eslint '**/*.ts'",
10 | "format": "prettier --write '**/*.{ts,tsx}'",
11 | "test": "jest",
12 | "test:watch": "jest --watch",
13 | "test:cov": "jest --coverage"
14 | },
15 | "dependencies": {
16 | "@zeit/next-css": "^1.0.1",
17 | "apollo-boost": "^0.4.3",
18 | "bootstrap": "^4.3.1",
19 | "graphql": "^14.4.2",
20 | "isomorphic-unfetch": "^3.0.0",
21 | "next": "^9.0.2",
22 | "react": "^16.8.6",
23 | "react-apollo": "^2.5.8",
24 | "react-bootstrap": "^1.0.0-beta.9",
25 | "react-dom": "^16.8.6",
26 | "styled-components": "^4.3.2"
27 | },
28 | "devDependencies": {
29 | "@types/enzyme": "^3.10.3",
30 | "@types/jest": "^24.0.15",
31 | "@types/node": "^12.6.8",
32 | "@types/react": "^16.8.23",
33 | "@types/styled-components": "^4.1.18",
34 | "@typescript-eslint/eslint-plugin": "^1.12.0",
35 | "@typescript-eslint/parser": "^1.12.0",
36 | "babel-eslint": "^10.0.2",
37 | "babel-jest": "^24.8.0",
38 | "enzyme": "^3.10.0",
39 | "enzyme-adapter-react-16": "^1.14.0",
40 | "enzyme-to-json": "^3.3.5",
41 | "eslint": "5.x.x",
42 | "eslint-config-prettier": "^6.0.0",
43 | "eslint-config-react-app": "^4.0.1",
44 | "eslint-plugin-flowtype": "^3.12.1",
45 | "eslint-plugin-import": "^2.18.2",
46 | "eslint-plugin-jsx-a11y": "^6.2.3",
47 | "eslint-plugin-prettier": "^3.1.0",
48 | "eslint-plugin-react": "^7.14.2",
49 | "eslint-plugin-react-hooks": "^1.6.1",
50 | "jest": "^24.8.0",
51 | "prettier": "^1.18.2",
52 | "ts-jest": "^24.0.2",
53 | "typescript": "^3.5.3"
54 | },
55 | "jest": {
56 | "setupFiles": [
57 | "/jest.setup.js"
58 | ],
59 | "testRegex": "(/test/.*|(\\.|/)(test|spec))\\.(jsx?|js?|tsx?|ts?)$",
60 | "transform": {
61 | "^.+\\.tsx?$": "babel-jest"
62 | },
63 | "testPathIgnorePatterns": [
64 | "/.next/",
65 | "/node_modules/"
66 | ],
67 | "moduleFileExtensions": [
68 | "ts",
69 | "tsx",
70 | "js",
71 | "jsx"
72 | ],
73 | "snapshotSerializers": [
74 | "enzyme-to-json/serializer"
75 | ]
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/packages/client/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { ApolloClient, NormalizedCacheObject } from 'apollo-boost'
2 | import 'bootstrap/dist/css/bootstrap.min.css'
3 | import App, { Container } from 'next/app'
4 | import * as React from 'react'
5 | import { ApolloProvider } from 'react-apollo'
6 | import { Theme, ThemeProvider } from 'styled-components'
7 | import withApollo from '../lib/withApollo'
8 |
9 | const theme: Theme = {
10 | colors: {
11 | primary: '#0070ff',
12 | secondary: '#111111',
13 | },
14 | }
15 |
16 | class AppClass extends App<{
17 | apolloClient: ApolloClient
18 | }> {
19 | render() {
20 | const { Component, pageProps, apolloClient } = this.props
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 | }
32 |
33 | export default withApollo(AppClass)
34 |
--------------------------------------------------------------------------------
/packages/client/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import Document, { DocumentContext } from 'next/document'
2 | import * as React from 'react'
3 | import { ServerStyleSheet } from 'styled-components'
4 |
5 | class DocumentClass extends Document {
6 | static async getInitialProps(context: DocumentContext) {
7 | const sheet = new ServerStyleSheet()
8 | const originalRenderPage = context.renderPage
9 | try {
10 | context.renderPage = () =>
11 | originalRenderPage({
12 | enhanceApp: App => props => sheet.collectStyles(),
13 | })
14 | const initialProps = await Document.getInitialProps(context)
15 | return {
16 | ...initialProps,
17 | styles: (
18 | <>
19 | {initialProps.styles}
20 | {sheet.getStyleElement()}
21 | >
22 | ),
23 | }
24 | } finally {
25 | sheet.seal()
26 | }
27 | }
28 | }
29 |
30 | export default DocumentClass
31 |
--------------------------------------------------------------------------------
/packages/client/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag'
2 | import React from 'react'
3 | import { Query } from 'react-apollo'
4 | import { Button, Container } from 'react-bootstrap'
5 | import styled from 'styled-components'
6 |
7 | const Wrapper = styled(Container)`
8 | margin-top: 70px;
9 | `
10 |
11 | const Text = styled('h1')`
12 | color: ${({ theme }) => theme.colors.primary};
13 | text-align: center;
14 | margin-bottom: 25px;
15 | `
16 |
17 | const ButtonsWrapper = styled('div')`
18 | display: flex;
19 | justify-content: center;
20 | `
21 |
22 | const query = gql`
23 | {
24 | getHello
25 | }
26 | `
27 |
28 | interface Data {
29 | getHello: string
30 | }
31 |
32 | const Home: React.FC = () => {
33 | return (
34 |
35 | query={query}>
36 | {({ error, loading, data }) => (
37 | <>
38 | {error && {error}
}
39 | {loading && {loading}
}
40 | {data && {data.getHello}}
41 | >
42 | )}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
56 | export default Home
57 |
--------------------------------------------------------------------------------
/packages/client/styled.d.ts:
--------------------------------------------------------------------------------
1 | import 'styled-components'
2 |
3 | declare module 'styled-components' {
4 | export interface Theme {
5 | colors: {
6 | primary: string
7 | secondary: string
8 | }
9 | }
10 | }
11 |
12 | declare module 'flwww'
13 |
--------------------------------------------------------------------------------
/packages/client/test/index.spec.tsx:
--------------------------------------------------------------------------------
1 | import { shallow } from 'enzyme'
2 | import * as React from 'react'
3 | import Home from '../pages/index'
4 |
5 | describe('Index Page', () => {
6 | it('renders the h1 element', () => {
7 | const Component = shallow()
8 | expect(Component.find('h1').text()).toEqual('Hello World')
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/packages/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve"
16 | },
17 | "exclude": ["node_modules", "**/*.spec.tsx", "**/*.spec.ts"],
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/server/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "plugin:@typescript-eslint/recommended",
5 | "prettier/@typescript-eslint",
6 | "plugin:prettier/recommended"
7 | ],
8 | "plugins": ["@typescript-eslint"],
9 | "rules": {
10 | "@typescript-eslint/indent": "off",
11 | "@typescript-eslint/no-parameter-properties": "off",
12 | "@typescript-eslint/explicit-member-accessibility": "off",
13 | "@typescript-eslint/explicit-function-return-type": "off"
14 | },
15 | "env": {
16 | "es6": true,
17 | "node": true
18 | },
19 | "globals": {
20 | "Atomics": "readonly",
21 | "SharedArrayBuffer": "readonly"
22 | },
23 | "parserOptions": {
24 | "ecmaVersion": 2018,
25 | "sourceType": "module"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/server/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twetlix/nestjs-react-graphql-boilerplate/d097b19563cea29670d3b2d2bb994724ad7aa29d/packages/server/README.md
--------------------------------------------------------------------------------
/packages/server/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "language": "ts",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/server/nodemon-debug.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": ["src"],
3 | "ext": "ts",
4 | "ignore": ["src/**/*.spec.ts"],
5 | "exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/server/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": ["src"],
3 | "ext": "ts",
4 | "ignore": ["src/**/*.spec.ts"],
5 | "exec": "ts-node -r tsconfig-paths/register src/main.ts"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@nestjs-react-graphql-boilerplate/server",
3 | "version": "0.0.1",
4 | "description": "The nestjs backend",
5 | "author": "Twetlix",
6 | "license": "MIT",
7 | "scripts": {
8 | "dev": "NODE_ENV=development nodemon",
9 | "prod": "NODE_ENV=production node dist/main.js",
10 | "build": "NODE_ENV=production tsc -p tsconfig.build.json",
11 | "format": "prettier --write \"src/**/*.ts\"",
12 | "start": "NODE_ENV=development ts-node -r tsconfig-paths/register src/main.ts",
13 | "prestart:prod": "rimraf dist && npm run build",
14 | "lint": "eslint '**/*.ts'",
15 | "test": "NODE_ENV=test jest",
16 | "test:watch": "NODE_ENV=test jest --watch",
17 | "test:cov": "NODE_ENV=test jest --coverage",
18 | "test:debug": "NODE_ENV=test node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
19 | "test:e2e": "NODE_ENV=test jest --config ./test/jest-e2e.json"
20 | },
21 | "dependencies": {
22 | "@nestjs/common": "^6.5.3",
23 | "@nestjs/core": "^6.5.3",
24 | "@nestjs/graphql": "^6.4.2",
25 | "@nestjs/platform-express": "^6.5.3",
26 | "@nestjs/typeorm": "^6.1.3",
27 | "@types/dotenv": "^6.1.1",
28 | "apollo-server-express": "^2.7.0",
29 | "class-transformer": "^0.2.3",
30 | "dotenv": "^8.0.0",
31 | "graphql": "^14.4.2",
32 | "graphql-tools": "^4.0.5",
33 | "helmet": "^3.19.0",
34 | "pg": "^7.11.0",
35 | "reflect-metadata": "^0.1.13",
36 | "rimraf": "^2.6.3",
37 | "rxjs": "^6.5.2",
38 | "type-graphql": "^0.17.4",
39 | "typeorm": "^0.2.18"
40 | },
41 | "devDependencies": {
42 | "@nestjs/testing": "^6.5.3",
43 | "@types/express": "^4.17.0",
44 | "@types/helmet": "^0.0.43",
45 | "@types/jest": "^24.0.15",
46 | "@types/node": "^12.6.8",
47 | "@types/supertest": "^2.0.8",
48 | "@typescript-eslint/eslint-plugin": "^1.12.0",
49 | "@typescript-eslint/parser": "^1.12.0",
50 | "eslint": "^6.0.1",
51 | "eslint-config-prettier": "^6.0.0",
52 | "eslint-plugin-prettier": "^3.1.0",
53 | "jest": "^24.8.0",
54 | "nodemon": "^1.19.1",
55 | "prettier": "^1.18.2",
56 | "supertest": "^4.0.2",
57 | "ts-jest": "^24.0.2",
58 | "ts-node": "^8.3.0",
59 | "tsconfig-paths": "^3.8.0",
60 | "typescript": "^3.5.3"
61 | },
62 | "jest": {
63 | "moduleFileExtensions": [
64 | "js",
65 | "json",
66 | "ts"
67 | ],
68 | "rootDir": "src",
69 | "testRegex": ".spec.ts$",
70 | "transform": {
71 | "^.+\\.(t|j)s$": "ts-jest"
72 | },
73 | "coverageDirectory": "../coverage",
74 | "testEnvironment": "node"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/packages/server/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common'
2 | import { GraphQLModule } from '@nestjs/graphql'
3 | import { TypeOrmModule } from '@nestjs/typeorm'
4 | import { ConfigModule } from './common/config/config.module'
5 | import { ConfigService } from './common/config/config.service'
6 | import { UserEntity } from './user/user.entity'
7 | import { UserModule } from './user/user.module'
8 |
9 | @Module({
10 | imports: [
11 | TypeOrmModule.forRootAsync({
12 | imports: [ConfigModule],
13 | useFactory: (configService: ConfigService) => ({
14 | type: 'postgres',
15 | url: configService.get('DB_URL'),
16 | synchronize: true,
17 | logging: process.env.NODE_ENV === 'development' ? true : false,
18 | dropSchema: process.env.NODE_ENV === 'test' ? true : false,
19 | entities: [UserEntity],
20 | migrations: [],
21 | subscribers: [],
22 | }),
23 | inject: [ConfigService],
24 | }),
25 | GraphQLModule.forRoot({
26 | autoSchemaFile: 'src/schema.gql',
27 | playground: true,
28 | debug: process.env.NODE_ENV === 'development' ? true : false,
29 | context: ({ req }) => ({ req }),
30 | }),
31 | UserModule,
32 | ],
33 | })
34 | export class AppModule {}
35 |
--------------------------------------------------------------------------------
/packages/server/src/common/config/config.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common'
2 | import { ConfigService } from './config.service'
3 |
4 | @Module({
5 | providers: [
6 | {
7 | provide: ConfigService,
8 | useValue: new ConfigService(`.env.${process.env.NODE_ENV}`),
9 | },
10 | ],
11 | exports: [ConfigService],
12 | })
13 | export class ConfigModule {}
14 |
--------------------------------------------------------------------------------
/packages/server/src/common/config/config.service.ts:
--------------------------------------------------------------------------------
1 | import * as dotenv from 'dotenv'
2 | import * as fs from 'fs'
3 |
4 | export class ConfigService {
5 | private readonly envConfig: { [key: string]: string }
6 |
7 | constructor(filePath: string) {
8 | this.envConfig = dotenv.parse(fs.readFileSync(filePath))
9 | }
10 |
11 | get(key: string): string {
12 | return this.envConfig[key]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/server/src/main.ts:
--------------------------------------------------------------------------------
1 | import { Logger, ValidationPipe } from '@nestjs/common'
2 | import { NestFactory } from '@nestjs/core'
3 | import * as helmet from 'helmet'
4 | import { AppModule } from './app.module'
5 | import { ConfigService } from './common/config/config.service'
6 |
7 | class App {
8 | constructor(private readonly configService: ConfigService) {
9 | this.run()
10 | }
11 |
12 | async run(): Promise {
13 | const port = this.configService.get('PORT') || 3000
14 | const app = await NestFactory.create(AppModule)
15 | app.use(helmet())
16 | app.useGlobalPipes(
17 | new ValidationPipe({
18 | skipMissingProperties: true,
19 | }),
20 | )
21 | await app.listen(port)
22 | Logger.log(`Server started on http://localhost:${port}/graphql`, 'Server')
23 | }
24 | }
25 |
26 | new App(new ConfigService(`.env.${process.env.NODE_ENV}`))
27 |
--------------------------------------------------------------------------------
/packages/server/src/schema.gql:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------
2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
4 | # -----------------------------------------------
5 |
6 | type Mutation {
7 | createUser(password: String!, email: String!, name: String!): UserEntity!
8 | }
9 |
10 | type Query {
11 | getHello: String!
12 | findAllUsers: [UserEntity!]!
13 | }
14 |
15 | type UserEntity {
16 | userId: String!
17 | name: String!
18 | email: String!
19 | }
20 |
--------------------------------------------------------------------------------
/packages/server/src/user/user.entity.ts:
--------------------------------------------------------------------------------
1 | import { Field, ObjectType } from 'type-graphql'
2 | import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
3 |
4 | @Entity()
5 | @ObjectType()
6 | export class UserEntity {
7 | @PrimaryGeneratedColumn('uuid')
8 | @Field()
9 | userId: string
10 |
11 | @Column()
12 | @Field()
13 | name: string
14 |
15 | @Column()
16 | @Field()
17 | email: string
18 |
19 | @Column()
20 | password: string
21 | }
22 |
--------------------------------------------------------------------------------
/packages/server/src/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common'
2 | import { TypeOrmModule } from '@nestjs/typeorm'
3 | import { UserEntity } from './user.entity'
4 | import { UserResolver } from './user.resolver'
5 | import { UserService } from './user.service'
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([UserEntity])],
9 | providers: [UserService, UserResolver],
10 | })
11 | export class UserModule {}
12 |
--------------------------------------------------------------------------------
/packages/server/src/user/user.resolver.ts:
--------------------------------------------------------------------------------
1 | import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'
2 | import { UserEntity } from './user.entity'
3 | import { UserService } from './user.service'
4 |
5 | @Resolver()
6 | export class UserResolver {
7 | constructor(private readonly userService: UserService) {}
8 |
9 | @Query(() => String)
10 | getHello() {
11 | return 'Hello World From Nestjs/GraphQL'
12 | }
13 |
14 | @Query(() => [UserEntity])
15 | findAllUsers() {
16 | return this.userService.findAll()
17 | }
18 |
19 | @Mutation(() => UserEntity)
20 | createUser(
21 | @Args('name') name: string,
22 | @Args('email') email: string,
23 | @Args('password') password: string,
24 | ) {
25 | return this.userService.createOne({ name, email, password })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/server/src/user/user.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common'
2 | import { InjectRepository } from '@nestjs/typeorm'
3 | import { Repository } from 'typeorm'
4 | import { UserEntity } from './user.entity'
5 |
6 | @Injectable()
7 | export class UserService {
8 | constructor(
9 | @InjectRepository(UserEntity)
10 | private readonly userRepository: Repository,
11 | ) {}
12 |
13 | findAll() {
14 | return this.userRepository.find()
15 | }
16 |
17 | async createOne(data: any) {
18 | const user = await this.userRepository.create(data)
19 | await this.userRepository.save(user)
20 | return this.userRepository.findOne({ where: { email: data.email } })
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/server/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { NestApplication } from '@nestjs/core'
2 | import { Test, TestingModule } from '@nestjs/testing'
3 | import * as request from 'supertest'
4 | import { AppModule } from './../src/app.module'
5 |
6 | describe('AppController (e2e)', (): void => {
7 | let app: NestApplication
8 |
9 | beforeEach(
10 | async (): Promise => {
11 | const moduleFixture: TestingModule = await Test.createTestingModule({
12 | imports: [AppModule],
13 | }).compile()
14 |
15 | app = moduleFixture.createNestApplication()
16 | await app.init()
17 | },
18 | )
19 |
20 | it('/ (GET)', (): Test => {
21 | return request(app.getHttpServer())
22 | .get('/')
23 | .expect(200)
24 | .expect('Hello World!')
25 | })
26 | })
27 |
--------------------------------------------------------------------------------
/packages/server/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/server/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "target": "es6",
9 | "sourceMap": true,
10 | "outDir": "./dist",
11 | "baseUrl": "./"
12 | },
13 | "exclude": ["node_modules"]
14 | }
15 |
--------------------------------------------------------------------------------