using "cool-fruit-app" and a "banana" model for the example
mkdir cool-fruit-app
cd cool-fruit-app
mkdir cool-fruit-app-frontend
cd cool-fruit-app-frontend
yarn init
yarn add react
npm install [email protected] [email protected]
yarn add react-scripts
touch .gitignore
git init
code .
# dependencies
/node_modules
# production
/build
/.netlify
# logging
*.log
# misc
.DS_Store
mkdir cool-fruit-app-backend
cd cool-fruit-app-backend
npm init
touch .gitignore
git init
code .
# dependencies
/node_modules
# production
/build
# environment variables
.env
# logging
*.log
# misc
.DS_Store
7. confirm packages and change scripts to get frontend package.json looking like so (with latest versions)
{
"name": "cool-fruit-app-frontend",
"version": "1.0.0",
"description": "cool-fruit-app app frontend in react",
"main": "index.js",
"author": "Levente Toth",
"license": "MIT",
"dependencies": {
"axios": "^0.19.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-scripts": "^3.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"deploy": "yarn build && netlify deploy --prod --dir=build"
}
}
8. confirm packages and change scripts to get backend package.json looking like so (with latest versions)
{
"name": "cool-fruit-app-backend",
"version": "1.0.0",
"description": "banana app backend in express, connecting to MongoDB Atlas",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js",
"dev": "nodemon index.js"
},
"author": "Levente Toth",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"mongoose": "^5.8.6",
"morgan": "^1.9.1"
},
"devDependencies": {
"nodemon": "^2.0.2"
}
}
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const morgan = require("morgan");
require("dotenv").config();
const app = express();
// this short circuit is here for using the deployed env vairiable of heroku, or 5000 if running locally
const PORT = process.env.PORT || 5000;
const DB_URL = process.env.DB_URL;
const dbConfig = { useNewUrlParser: true, useUnifiedTopology: true };
// create a list for the CORS whitelist, do this so we can add the deployed site later
const whitelist = ["http://localhost:3000"];
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
};
mongoose.connect(DB_URL, dbConfig, (err) => {
if (err) {
console.log("MongooseDB Connection Error");
} else {
console.log("Connected to MongooseDB");
}
});
// middlewares:
// this is used for better logging in server terminal
app.use(morgan("dev"));
// this is needed for JSON to parse properly by post/put HTTP actions
app.use(express.json());
// this allows external addresses to access the website, generally keep it restricted to the front end apps address (wether that is the local address or deployed one while still developing) IMPORTANT: this cors policy middlware must be called before the routes middleware, otherwise it won't be applied to those routes cause CORS bugs
app.use(cors(corsOptions));
// connect with index.js file in routes directory
app.use(require("./routes/index"));
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bananaSchema = new Schema(
{
title: String,
description: String,
},
{
timestamps: true,
}
);
const Banana = mongoose.model("Banana", bananaSchema);
module.exports = Banana;
3. create a controller file banana-controller.js
, include a simple get and post actions to start, to prove database working as expected
// import the model these actions control
const Banana = require("../models/banana");
const index = async (req, res) => {
try {
const bananas = await Banana.find();
res.send(bananas);
} catch (err) {
res.status(404).send(err);
}
};
const create = async (req, res) => {
const { title, description } = req.body;
try {
const newBanana = await Banana.create({ title, description });
res.send(newBanana);
} catch (err) {
res.status(400).send(err);
}
};
module.exports = {
index,
create,
};
- make a routes folder
- make a routes
index.js
to collate your routes
const express = require("express");
const router = express.Router();
const index = (req, res) => {
res.send("hello world");
};
router.get("/", index);
// define routes here, this will mean anything in the banana-routes.js file (yet to be created) will assume that /banana will prepend the route path defined in banana-routes.js
router.use("/bananas", require("./bananas-routes"));
module.exports = router;
- make a routes file
banana-routes.js
for your first group of routes
const express = require("express");
const router = express.Router();
// import actions from controller
const { index, create } = require("../controllers/bananas-controller");
// the full address will be localhost:5000/bananas to reach these two routes/actions, as defined by the router.use call in the routes/index.js file
router.get("/", index);
router.post("/", create);
module.exports = router;
5. create a .env file for environment variables, here we will store the MongoDB address, for now we will just use a local database, later in heroku we will define the environment variable in the heroku dashboard, and use a Mongo Atlas address
DB_URL=mongodb://localhost:27017/cool-fruit-app
- run the express server (using nodemon via
yarn dev
ornpm run dev
) - header must include
key: Content-Type
andvalue: application/json
- POST to localhost:5000/bananas
{
"title": "banana split sundae",
"description": "basic banana dessert"
}
- GET to localhost:5000/bananas response to expect:
[
{
"_id": "5e180c805e93b25201a94234",
"title": "banana split sundae",
"description": "basic banana dessert",
"createdAt": "2020-01-10T05:32:48.127Z",
"updatedAt": "2020-01-10T05:32:48.127Z",
"__v": 0
}
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>MERN Boilerplate by Levente Toth</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App.jsx";
ReactDOM.render(<App />, document.getElementById("root"));
4. create a .env.development file for environment variables, here we will store the address of our express server, for now we will just use a local database, later in when we deploy the frontend to netlify, and have our backend deployed to heroku, we will use a .env file that contains the heroku deployed address. make sure the port matches the one currently being used to run the express server
REACT_APP_BACKEND_URL=http://localhost:5000/
5. run the react app using yarn start
, press Y
when it asks about detecting target browsers. make sure react is running on port 3000 since that is the address we unblocked in cors in the express server
1. install the heroku CLI (if you don't have it already) and login (make sure you have a heroku account) so that we can use heroku
commands in our terminal
heroku create
git push heroku master
- go to your mongoDB atlas account and get the link of the cluster you have created (create one if you havn't already, also make sure there is a user with read/write access to any database you create)
[connect]
[connect your application]
- copy paste this link somewhere, and swap out the username and password, as well as the database "test" to be the name of the database you want your app to be using (it doesn't have to already exist, mongoDB will create one automatically if it is not.
- go to the heroku website
- enter the dashboard for the app you just created
- settings
- reveal config vars
- enter DB_URL and the link you just copied and modified into the Key and Value fields
- do exactly the same as earlier, but replace the localhost address with the heroku deployed website URL as given in the heroku web dashboard, expect the same results
1. install the netlify CLI (if you don't have it already) and login (make sure you have a netlify account) so that we can use netlify
commands in our terminal
REACT_APP_BACKEND_URL=https://cool-fruit-app.herokuapp.com
3. create a file in the root
called netlify.toml
this is just some extra config needed by netlify for a build and deploy to work correctly
[[redirects]]
from = "/*"
to= "/index.html"
status = 200
- yarn deploy
...
const whitelist = ['http://localhost:3000', 'https://cool-fruit-app.netlify.com'];
...