This boilerplate is the foundation for a marketplace application. Users can create accounts, add products they own to their collection.
- React + Redux to manager UI state
- Firebase cloud function hosts GraphQL server
- Apollo is integrated into React
- Heroku hosts the frontend & backend
- Firebase Firestore hosts the database
- Firebase Auth handles authentication
- Yarn workspaces divides the app between frontend and backend to help manage dependencies
- Lunr Search
- i18n helper to make the hard coded copy easier to translate
- Add analyzer to review bundle sizes
- Convert to use Typescript
- NODE 10.18.1 because (at the time) this was the maximum node version allowed by Google Cloud Platform.
- Install VS Code IDE (optional, you can use an IDE of choice but we used VSCode while building this app)
- Install iTerm2 (optional, convenient way to style your terminal)
- Install Oh My Zsh (optional, lets you easily style your terminal)
- Install Homebrew
- Install NVM
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
- Install NVM continued: Update
~/.zshrc
(this may happen automatically)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
- Install NODE using NVM
$ nvm install 10.18.1
$ nvm use 10.18.1
- Install Yarn using Homebrew
$ brew install yarn
- Install Firebase CLI
$ npm install firebase -g
$ npm install -g firebase-tools
- Install Heroku CLI using Homebrew
$ brew tap heroku/brew && brew install heroku
- Install Apollo CLI
$ yarn global add apollo
- Install VS Code Apollo
- Clone the repo
$ git clone git@github.com/adriaanbalt/boilerplate-react-firebase-graphql.git
- Install dependencies from the app directory (boilerplate-react-firebase-graphql/app)
yarn run install
- Make sure you are using the correct node version
$ nvm use 10.18.1
- Make sure you are logged into our private Firebase account development account
$ firebase login
- In another terminal window, start the local backend development server
$ yarn run backend-start
- In one terminal window, start the local frontend development server
$ yarn run frontend-start
- Open browser to http://localhost:3000 (this will happen automatically)
- API GraphQL "emulator" server will be hosted at http://localhost:5000 Note: Local development is connected to production database and storage on Firebase! (add item local, item is also in prod)
To update the production app both the frontend and backend must be on the server. To make this easy, there is one terminal command that updates both:
$ yarn deploy
- Merge to the frontend code to the
master
branch
$ git add .
$ git commit -m 'YOUR CUSTOM COMMIT MESSAGE HERE'
$ git push origin master
-
Check that the environments are building Frontend Activity Monitor Frontend Activity Monitor
-
Deploy the GraphQL Mutation and Query node js files
From the packages/backend
folder run this command
$ firebase deploy --only functions
This will deploy the api
function which will update the backend requests on the Google Cloud server.
$ npx apollo service:push --endpoint=http://localhost:5000
$ firebase use --add fireplay-app
$ firebase setup:emulators:firestore
$ firebase emulators:start --only firestore // starts firestore local environment
- Note: this has been completed but is here for reference should anything need adjustment
Note: this may not be necessary but the documenation includes it.
$ yarn config set workspaces-experimental true
Useful links:
- https://yarnpkg.com/blog/2017/08/02/introducing-workspaces/
- https://doppelmutzi.github.io/monorepo-lerna-yarn-workspaces/
root
├── package.json
├── packages
│ ├── backend
│ │ ├── package.json
│ ├── frontend
│ │ ├── package.json
└── yarn.lock
- Note: Node version 8 is specifically so we can use Firebase Cloud Functions. Otherwise this warning will be triggered:
'Warning, FIREBASE_CONFIG and GCLOUD_PROJECT environment variables are missing. Initializing firebase-admin will fail'
reference - Note: workspace modules package.json files must also be set to the same node version.
$ nvm use 10.18.1
{
"private": true,
"name": "boilerplate-react-firebase-graphql",
"workspaces": {
"packages": [
"packages/*"
]
},
"engines": {
"node": "8.*"
}
}
$ yarn workspaces info
{
"boilerplate-react-firebase-graphql-frontend": {
"location": "packages/frontend",
"workspaceDependencies": [],
"mismatchedWorkspaceDependencies": []
},
"boilerplate-react-firebase-graphql-backend": {
"location": "packages/backend",
"workspaceDependencies": [
"boilerplate-react-firebase-graphql-frontend"
],
"mismatchedWorkspaceDependencies": []
}
}
- Update frontend with request
- open packages/frontend/src/graphql/requests.js
- Create a new constant variable like below
export const NAME_OF_MUTATION = gql`
mutation NameOfMutation( $propertyToPass: DataType! ) { // using ! means that this property is required
NameOfMutation( propertyToPass: $propertyToPass ) {
propertyToPass,
}
}
`;
- Make sure "NameOfMutation" is the same as the next step
- Update resolver and schema in backend
- open packages/backend/src/resolvers.js
- inside the "mutation" property create a new function like below
NameOfMutation: async (_, data) => {
try {
// update database
const newDbObjId = await admin
.firestore()
.collection('CollectionName')
.add(data)
.then( (docRef) => {
return docRef.id
})
.catch( (error) => {
console.error("[addTransaction] Error adding document: ", error);
})
return objectAssignDeep({}, { id: newDbObjId }, data)
} catch (error) {
throw new ApolloError(error);
}
},
- open packages/backend/src/schema.js
- Create Heroku app using CLI
$ cd packages/frontend
$ heroku create boilerplate-react-firebase-graphql-frontend
$ heroku apps // check if the app installed (you should see a list of apps including one named boilerplate-react-firebase-graphql-frontend
- We are using Create React App aka "CRA"
$ cd packages/frontend
$ npx create-react-app .
$ yarn start // check if the app starts (this should open a browser to localhost:3000)
- Install buildpacks
$ heroku buildpacks:add -a boilerplate-react-firebase-graphql-frontend https://github.com/heroku/heroku-buildpack-multi-procfile
$ heroku buildpacks --app boilerplate-react-firebase-graphql-frontend // check to make sure the buildpack installed
- Setup config variables
$ heroku config:set PROCFILE=/packages/frontend/Procfile --app boilerplate-react-firebase-graphql-frontend
$ heroku config:set BUILD_ENV=frontend --app boilerplate-react-firebase-graphql-backend
- Create root/static.json
- Enables deep linking for the router on Heroku
{
"root": "packages/frontend/build/",
"clean_urls": false,
"routes": {
"/**": "index.html"
}
}
- Create Heroku app using CLI
$ cd packages/backend
$ heroku create boilerplate-react-firebase-graphql-backend
$ heroku apps // check if the app installed (you should see a list of apps including one named boilerplate-react-firebase-graphql-backend
- Create buildpacks for backend
$ heroku buildpacks:add -a boilerplate-react-firebase-graphql-backend https://github.com/heroku/heroku-buildpack-multi-procfile
$ heroku buildpacks --app boilerplate-react-firebase-graphql-backend // check to make sure the buildpack installed
- Setup config variables
$ heroku config:set PROCFILE=/packages/backend/Procfile --app boilerplate-react-firebase-graphql-backend
$ heroku config:set BUILD_ENV=backend --app boilerplate-react-firebase-graphql-backend
$ heroku config:set APOLLO_ENGINE_API_KEY={GET FROM APOLLO} --app boilerplate-react-firebase-graphql-backend
- Note: this runs both the frontend and backend build scripts within the heroku server
- Note:
bash
may not be required - Note:
./scripts/
is required to run this command because of how permissions are setup globally on Heroku
"scripts": {
"heroku-postbuild": "bash ./scripts/heroku.build",
}
- Add domain to heroku project, reference
$ cd packages/frontend
$ heroku domains:add -a boilerplate-react-firebase-graphql-frontend app.{your-domain}.com --remote prod
- Copy generated DNS target
$ heroku domains --app boilerplate-react-firebase-graphql-frontend
- Login to GoDaddy
- Go to DNS Management
- Create CNAME record in DNS manager with desired subdomain (in our case 'app' or 'api' in the case of the backend)
- Paste generated DNS target in the GoDaddy's CNAME record created in the previous step, reference
root
├── package.json
├── static.json // used by CRA
├── packages
│ ├── frontend
│ │ ├── package.json
│ │ ├── Procfile
│ ├── backend
│ │ ├── package.json
│ │ ├── Procfile
├── scripts // must be within this folder otherwise heroku will not give permission to run it
│ ├── heroku.build // used by heroku-postbuild step (see ${root}/package.json)
└── yarn.lock // you need this for Heroku to run YARN otherwise it will run NPM by default
- Deployment
From the packages/backend
folder run this command
$ firebase deploy --only functions
- List of Cloud Functions
This is where the GraphQL server is hosted.
When a new user is created, we need to update the Firestore database with user credentials. This appears to be the only way to store the uid
from the "authentication" tab in Firebase console.
This function only logs a statement that an object on storage has changed. The callback includes a reference to the object.
- See what is happening on the server
$ heroku logs --tail --app boilerplate-react-firebase-graphql-frontend // or boilerplate-react-firebase-graphql-backend
- Deploying to the Heroku remote will show you what is happening on the server in your terminal. You can also see this in the Activity dashboard if you login to Heroku ie https://dashboard.heroku.com/apps/boilerplate-react-firebase-graphql-frontend/activity. Keep in mind this is applicable to both frontend and backend applications.
$ git push heroku-frontend master
- Graph QL errors can happen. When they do, look at the Network tab in your Browser's Dev Inspector to find the broken request. You will find more information about the error there.
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
/globalStyles
is where universal files are located. They are loaded into /index.scss which does not includemodule
in its name so it does not act like a css module.- React directives can be found in the
/components
folder, including the screens themselves as their helper directives likeSearch
orDropDown
- There is an analytics middleware that is able to catch each action that is performed. As you can see in that file, some actions have been chosen to dispatch analytics events to the server (there is no server but this is where that functionality would exist)
- Using HOC to have inheritance between the screens, which helps with the animations as well as the responsiveness of the app. If I had more time I would further flesh out the animations.
- The data is stored within the
reducers
folder and is combined together in theStore.js
file. - Selectors are used to memoize and quickly retrieve state.
- Actions are available across the app located in the
actions
folder.
- There is a warning from router but the creators of React Router haven't fixed it yet, not sure why that is the case. See more here