├── .dockerignore ├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── docker-compose.yml ├── docs ├── azure.md ├── heroku.md ├── index.md └── zeit-now.md ├── frontend ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── scripts │ └── watch.js └── src │ ├── components │ ├── App.js │ ├── Footer.js │ ├── Header.js │ ├── HomePage.js │ ├── Input.css │ ├── Input.js │ ├── ListFooter.js │ ├── SignIn.css │ ├── SignIn.js │ ├── SignUp.css │ ├── SignUp.js │ └── TodoItem.js │ ├── index.js │ ├── logo.svg │ ├── queries │ └── todos.js │ └── styles │ └── index.css ├── go.mod ├── go.sum ├── heroku.yml ├── now.json └── src ├── config ├── environment.go ├── jwt.go └── pagination.go ├── db └── pg.go ├── dev └── watch-frontend.go ├── handler ├── lambda.go └── now.go ├── main.go ├── resolvers ├── hello.go ├── hello_test.go ├── todo.go └── user.go ├── runtime ├── handler.go └── jwt.go ├── structs ├── auth.go ├── todo.go └── user.go ├── test └── assert.go └── types ├── auth-response.go ├── mutation.go ├── query.go ├── schema.go ├── todo.go └── user.go /.dockerignore: -------------------------------------------------------------------------------- 1 | docs 2 | frontend/node_modules 3 | frontend/build -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | POSTGRESS_ADDRESS="localhost:54320" 2 | POSTGRESS_DATABASE="postgres" 3 | POSTGRESS_USER="postgres" 4 | POSTGRESS_PASSWORD="" 5 | JWT_SECRET="your-jwt-secret" 6 | GO_SERVES_STATIC="true" 7 | ENVIRONMENT="development" 8 | PORT=8080 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | **/*.exe 3 | frontend/build/ 4 | frontend/node_modules/ 5 | functions/ 6 | # Local Netlify folder 7 | .netlify 8 | main -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jacob9706/go-node-docker 2 | WORKDIR /app 3 | 4 | # Install dependencies 5 | COPY go.mod go.sum ./ 6 | RUN go mod download 7 | 8 | # Build the application and make executable 9 | COPY . . 10 | RUN go build -o ./main ./src/main.go 11 | RUN chmod +x ./main 12 | 13 | # Build the frontend 14 | RUN npm --prefix ./frontend install ./frontend 15 | RUN npm --prefix ./frontend run build 16 | 17 | EXPOSE 8080 18 | 19 | RUN wget -O /app/wait-for-it.sh https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh 20 | # Make db wait tool executable 21 | RUN chmod +x /app/wait-for-it.sh 22 | RUN apk add --no-cache bash 23 | 24 | CMD /app/wait-for-it.sh ${POSTGRESS_ADDRESS} -- ./main -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GO GraphQL Boilerplate 2 | 3 | A simple to use, deploys anywhere boilerplate to get you going in GO and GraphQL. 4 | 5 | Zeit Now Example: https://go-graphql-boilerplate.jacob-ebey.now.sh/ 6 | 7 | Heroku Example: https://go-graphql-boilerplate.herokuapp.com/ 8 | 9 | 10 | ## Getting started 11 | 12 | Get started by installing GO and Node(NPM), then cloneing this repo. 13 | 14 | Once cloned, install the GO dependencies: 15 | 16 | ```shell 17 | > go get ./... 18 | ``` 19 | 20 | CD into the `frontend` directory and install the NPM dependencies: 21 | 22 | ```shell 23 | > npm install 24 | ``` 25 | 26 | 27 | ## Dev mode 28 | 29 | Create A .env file in the root of the project, feel free to copy or rename the .env.example file. This is pre-configured for use with the docker-compose.yml file provied. 30 | 31 | Configure a combination of the following properties: 32 | 33 | ``` 34 | DATABASE_URL="postgres://postgres@localhost:54320/postgres" 35 | POSTGRESS_ADDRESS="localhost:54320" 36 | JWT_SECRET="your-jwt-secret" 37 | GO_SERVES_STATIC="true" 38 | ENVIRONMENT="development" 39 | PORT="8080" 40 | ``` 41 | 42 | OR 43 | 44 | ``` 45 | POSTGRESS_ADDRESS="localhost:54320" 46 | POSTGRESS_DATABASE="postgres" 47 | POSTGRESS_USER="postgres" 48 | POSTGRESS_PASSWORD="" 49 | JWT_SECRET="your-jwt-secret" 50 | GO_SERVES_STATIC="true" 51 | ENVIRONMENT="development" 52 | PORT="8080" 53 | ``` 54 | 55 | Both of the above configs are valid for the docker-compose.yml file provided. To spin up your DB, run: 56 | 57 | ```bash 58 | > docker-compose up -d 59 | ``` 60 | 61 | Now we can start the GO backend. In if `ENVIRONMENT` is set to development, we will get auto rebuilding of the frontend. 62 | 63 | 64 | ## Production 65 | 66 | You have a few options for deploying the boilerplate out of the box: 67 | 68 | - [Azure](docs/azure.md) 69 | - [Heroku](docs/heroku.md) 70 | - [Zeit Now](docs/zeit-now.md) 71 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | db: 4 | image: "postgres:11" 5 | container_name: "go-graphql-boilerplate-postgress" 6 | environment: 7 | POSTGRES_ENABLE_SSL: "true" 8 | ports: 9 | - "54320:5432" 10 | volumes: 11 | - my_dbdata:/var/lib/postgresql/data 12 | volumes: 13 | my_dbdata: 14 | -------------------------------------------------------------------------------- /docs/azure.md: -------------------------------------------------------------------------------- 1 | # Deploying to azure 2 | 3 | ## Publishing to Docker 4 | ```bash 5 | > docker build . -t jacob9706/go-graphql-boilerplate 6 | ``` 7 | 8 | ```bash 9 | > docker push jacob9706/go-graphql-boilerplate 10 | ``` 11 | 12 | ## Create a resource group 13 | 14 | ```bash 15 | > az group create --name go-graphql-boilerplate-rg --location "West US" 16 | ``` 17 | 18 | ## Create a service plan 19 | 20 | ```bash 21 | > az appservice plan create --name go-graphql-boilerplate-sp --resource-group go-graphql-boilerplate-rg --sku F1 --is-linux 22 | ``` 23 | 24 | ## Create the web app 25 | 26 | ```bash 27 | > az webapp create --resource-group go-graphql-boilerplate-rg --plan go-graphql-boilerplate-sp --name go-graphql-boilerplate --deployment-container-image-name jacob9706/go-graphql-boilerplate 28 | ``` 29 | 30 | # Configure envrionment variables 31 | 32 | ```bash 33 | > az webapp config appsettings set --name go-graphql-boilerplate --resource-group go-graphql-boilerplate-rg --settings DATABASE_URL='postgres://username:password@host:5432/dbname' POSTGRESS_ADDRESS='host:5432' GO_SERVES_STATIC='true' ENVIRONMENT='production' JWT_SECRET='your-jwt-secret' 34 | ``` 35 | 36 | # View the status of the deployment 37 | 38 | ```bash 39 | > az webapp log tail --name go-graphql-boilerplate --resource-group go-graphql-boilerplate-rg 40 | ``` -------------------------------------------------------------------------------- /docs/heroku.md: -------------------------------------------------------------------------------- 1 | # Deploying to Heroku 2 | 3 | ## Create a heroku application (called go-graphql-boilerplate in this case) 4 | 5 | ```bash 6 | > heroku create -s container go-graphql-boilerplate 7 | ``` 8 | 9 | ## Provision database 10 | 11 | ```bash 12 | > heroku addons:create heroku-postgresql:hobby-dev 13 | ``` 14 | 15 | ## Configure environment variables 16 | 17 | ```bash 18 | > heroku config:set ENVIRONMENT="production" 19 | > heroku config:set IS_HEROKU=true 20 | > heroku config:set GO_SERVES_STATIC=true 21 | > heroku config:set JWT_SECRET="your-jwt-secret" 22 | ``` 23 | 24 | Even though the above database provisioning command added the DATABASE_URL environment variable for us, 25 | the container startup process requies the host of the DB as another environment variable to verify that 26 | it is up. To get your DATABASE_URL to figure out the DB host execute: 27 | 28 | ```bash 29 | > heroku config:get DATABASE_URL 30 | ``` 31 | 32 | This will give you a connection string similar to: 33 | 34 | ```bash 35 | > postgres://username:password@host:5432/dbname 36 | ``` 37 | 38 | We will extract the `host:5432` portion directly after the `@` and set it as our POSTGRESS_ADDRESS 39 | environment variable: 40 | 41 | ```bash 42 | > heroku config:set POSTGRESS_ADDRESS="host:5432" 43 | ``` 44 | 45 | ## Deploy the application 46 | 47 | ```bash 48 | > git push heroku master 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # GO GraphQL Boilerplate 2 | 3 | A simple to uses, deploys anywhere boilerplate to get you going in GO and GraphQL. 4 | 5 | Example: https://go-graphql-boilerplate.herokuapp.com/ 6 | 7 | 8 | ## Getting started 9 | 10 | Get started by installing GO and Node(NPM), then cloneing this repo. 11 | 12 | Once cloned, install the GO dependencies: 13 | 14 | ```shell 15 | > go get ./... 16 | ``` 17 | 18 | CD into the `frontend` directory and install the NPM dependencies: 19 | 20 | ```shell 21 | > npm install 22 | ``` 23 | 24 | 25 | ## Dev mode 26 | 27 | Create A .env file in the root of the project, feel free to copy or rename the .env.example file. This is pre-configured for use with the docker-compose.yml file provied. 28 | 29 | Configure a combination of the following properties: 30 | 31 | ``` 32 | DATABASE_URL="postgres://postgres@localhost:54320/postgres" 33 | POSTGRESS_ADDRESS="localhost:54320" 34 | JWT_SECRET="your-jwt-secret" 35 | GO_SERVES_STATIC="true" 36 | ENVIRONMENT="development" 37 | PORT="8080" 38 | ``` 39 | 40 | OR 41 | 42 | ``` 43 | POSTGRESS_ADDRESS="localhost:54320" 44 | POSTGRESS_DATABASE="postgres" 45 | POSTGRESS_USER="postgres" 46 | POSTGRESS_PASSWORD="" 47 | JWT_SECRET="your-jwt-secret" 48 | GO_SERVES_STATIC="true" 49 | ENVIRONMENT="development" 50 | PORT="8080" 51 | ``` 52 | 53 | Both of the above configs are valid for the docker-compose.yml file provided. To spin up your DB, run: 54 | 55 | ```bash 56 | > docker-compose up -d 57 | ``` 58 | 59 | Now we can start the GO backend. In if `ENVIRONMENT` is set to development, we will get auto rebuilding of the frontend. 60 | 61 | 62 | ## Production 63 | 64 | You have a few options for deploying the boilerplate out of the box: 65 | 66 | - [Azure](azure) 67 | - [Heroku](heroku) 68 | - [Zeit Now](zeit-now) 69 | -------------------------------------------------------------------------------- /docs/zeit-now.md: -------------------------------------------------------------------------------- 1 | # Deploying to Zeit Now 2 | 3 | Zeit deployment is stupid simple, just do it: 4 | 5 | ```bash 6 | > now -n go-graphql-boilerplate -e DATABASE_URL="postgres://username:password@host:5432/dbname" -e ENVIRONMENT="production" -e JWT_SECRET="your-jwt-secret" 7 | ``` -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-graphql-boilerplate-frontend", 3 | "dependencies": { 4 | "@apollo/react-hooks": "3.1.3", 5 | "apollo-boost": "0.4.4", 6 | "graphql": "14.5.8", 7 | "jwt-decode": "2.2.0", 8 | "react": "16.10.2", 9 | "react-dom": "16.10.2", 10 | "react-router-dom": "^5.1.2", 11 | "react-scripts": "2.1.8", 12 | "todomvc-app-css": "2.3.0" 13 | }, 14 | "devDependencies": { 15 | "colors": "1.4.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom", 21 | "eject": "react-scripts eject" 22 | }, 23 | "browserslist": [ 24 | ">0.2%", 25 | "not dead", 26 | "not ie <= 11", 27 | "not op_mini all" 28 | ], 29 | "proxy": "http://localhost:8080" 30 | } 31 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-ebey/go-graphql-boilerplate/887b89f9c7da2afcb3083361f525843efbc907b8/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/scripts/watch.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'development'; 2 | 3 | const fs = require('fs-extra'); 4 | const paths = require('react-scripts/config/paths'); 5 | const webpack = require('webpack'); 6 | const importCwd = require('import-cwd'); 7 | const config = importCwd('react-scripts/config/webpack.config')('production') 8 | const colors = require('colors'); 9 | 10 | var entry = config.entry; 11 | var plugins = config.plugins; 12 | 13 | entry = entry.filter(fileName => !fileName.match(/webpackHotDevClient/)); 14 | plugins = plugins.filter(plugin => !(plugin instanceof webpack.HotModuleReplacementPlugin)); 15 | 16 | config.entry = entry; 17 | config.plugins = plugins; 18 | 19 | console.log("Building Frontend".bold.green) 20 | 21 | webpack(config).watch({}, (err, stats) => { 22 | if (err) { 23 | console.error(err); 24 | } else { 25 | copyPublicFolder(); 26 | } 27 | console.log(stats.toString({ 28 | chunks: false, 29 | colors: true 30 | })); 31 | }); 32 | 33 | function copyPublicFolder() { 34 | fs.copySync(paths.appPublic, paths.appBuild, { 35 | dereference: true, 36 | filter: file => file !== paths.appHtml 37 | }); 38 | } -------------------------------------------------------------------------------- /frontend/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Switch, Route } from "react-router-dom"; 3 | import decode from "jwt-decode"; 4 | import { useApolloClient } from "@apollo/react-hooks" 5 | 6 | import Footer from "./Footer"; 7 | import Header from "./Header"; 8 | import HomePage from "./HomePage"; 9 | import SignUp from "./SignUp"; 10 | 11 | export default function App() { 12 | const initialLoggedIn = React.useMemo(() => { 13 | const auth = JSON.parse( 14 | localStorage.getItem("auth") || JSON.stringify(null) 15 | ); 16 | 17 | if (auth && auth.refreshToken) { 18 | const decoded = decode(auth.refreshToken); 19 | 20 | if (decoded && Date.now() < decoded.exp * 1000) { 21 | return true; 22 | } 23 | } 24 | 25 | return false; 26 | }, []); 27 | 28 | const [loggedIn, setLoggedIn] = React.useState(initialLoggedIn); 29 | 30 | const client = useApolloClient(); 31 | 32 | const onSignIn = React.useCallback(payload => { 33 | setLoggedIn(!!payload); 34 | localStorage.setItem("auth", JSON.stringify(payload)); 35 | }); 36 | 37 | const onSignOut = React.useCallback(() => { 38 | setLoggedIn(false); 39 | localStorage.removeItem("auth"); 40 | client.resetStore(); 41 | }); 42 | 43 | const WrappedHome = props => ( 44 | 45 | ); 46 | 47 | const WrappedSignUp = props => ; 48 | 49 | return ( 50 | 51 |
52 |
53 | 54 | 55 | 56 | 57 | 58 |
59 |