├── .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 | ![Language](https://img.shields.io/badge/language-TypeScript-blue.svg) 6 | ![Backend](https://img.shields.io/badge/backend-Nestjs-e0224e.svg) 7 | ![API](https://img.shields.io/badge/api-GraphQL-e535ab.svg) 8 | ![Database](https://img.shields.io/badge/database-TypeOrm-fb0902.svg) 9 | ![Frontend](https://img.shields.io/badge/frontend-Nextjs-0f70f3.svg) 10 | ![Testing](https://img.shields.io/badge/testing-Jest-954058.svg) 11 | ![GitHub](https://img.shields.io/github/license/twetlix/nestjs-react-graphql-boilerplate.svg) 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 | --------------------------------------------------------------------------------