Node-Express-MongoDB Project Setup with Heroku Deployment
Reference
June 19, 2018
Here is the step-by-step for creating a new Node.js app using Express and MongoDB. Because I did this a long time ago, and had to remember.
Quick Reference:
- Prerequisites
- Set Up Express
- Scaffold the Routing
- Set Up Views, Template Scaffold, & Static Files
- Add 404
- Add Environment Variables Manager
- Link MongoDB Database
- Deploy To Heroku
Prerequisites
- Update Node to Long Term Stable version (nvm installation is simplest method)
- Check npm version with
npm -v
and update if necessary - Check for nodemon with
nodemon -v
. If it’s not there, install it globally withnpm install -g nodemon
- Install MongoDB Community edition. You need to be able to successfully run a
mongod
server and access a mongo shell using themongo
command. Cloud Atlas is not required (ignore that in the MongoDB documentation). I wrote about this previously.
Set Up Express
- Create a project folder and
cd
into it:mkdir new-project
cd new-project - Initiate a git repository:
git init
- Another option for the previous 2 steps is to create an empty GitHub repo and clone it to your machine.
- Add
.gitignore
file to ignore system files like.DS_Store
, etc. - Add
.README
file, can be empty to start - Commit project folder as
Initial commit
and push to remote repo as necessary. From here no more commit/push instructions will be included, but commit changes often! - Run
npm init
and follow instructions (answer questions) to createpackage.json
file. Ornpm init -y
to skip the questionnaire.- NOTE: The steps below assume the entry point is set to
app.js
- NOTE: The steps below assume the entry point is set to
- Add
node_modules
to.gitignore
file - Install Express by running
npm install express --save
- Create
app.js
file in main project folder and add server boilerplate:var express = require("express");
var app = express();
app.get("/", function (req, res) {
res.send("hello world");
});
var port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("App running on port " + port);
}); - Save file then run
nodemon app.js
- Open
localhost:3000
in browser; if you see hello world, the basic setup was successful.
Scaffold the Routing
Rather than putting all of the routes in the main app.js
file, this can be modularized.
- Make a routing index file:
mkdir routes
touch routes/index.js - Add node modules and an exporter to
routes/index.js
, and move the routing code fromapp.js
to this file:var express = require("express");
var router = express.Router();
router.get("/", function (req, res) {
res.send("hello world");
});
module.exports = router; - Access and use the exporter in the root folder’s
app.js
file:// Require Routes
var indexRoutes = require("./routes/index");
// Run app
app.use(indexRoutes); - Run
nodemon app.js
and refresh the page atlocalhost:3000
; you should see the same hello world
Set Up Views, Template Scaffold, & Static Files
Express looks for everything it should display on the front end to be stored in a views
directory. We can also use the ejs package for templating. We also need to tell Express where to find static files like CSS, images, and local JavaScript.
In root project folder run
npm install ejs --save
Tell Express to use ejs in the
/app.js
file:app.set("view engine", "ejs");
Set up the basic scaffolding for the page templates:
mkdir views
touch views/index.ejs
mkdir views/partials
touch views/partials/head.ejs
touch views/partials/header.ejs
touch views/partials/footer.ejsCreate the basic
views/index.ejs
file accessing the partials:<% include partials/head %>
<% include partials/header %>
<h4>This is where the main content will go</h4>
<% include partials/footer %>Set up the other partials with content in each, to ensure everything is linked:
views/partials/head.ejs:
<html>
<head>
<title>News App</title>
</head>views/partials/header.ejs
<body>
<header>
<nav>
<h1>This is where the navbar will go</h1>
</nav>
</header>
<main>views/partials/footer.ejs
</main>
<footer>
<h6>This is where the footer will go</h6>
</footer>
<!-- BEGIN SCRIPTS -->
</body>
</html>Tell Express where to access the static files and set up a home for each type:
/app.js:app.use(express.static(__dirname + "/public"));
From root directory in command line:
mkdir public
mkdir public/css
mkdir public/img
mkdir public/js
touch public/js/scripts.js
touch public/css/styles.cssAdd test CSS and JS code and then link their files to
views/partials/head.ejs
andviews/partials/footer.ejs
respectively:
public/js/scripts.js:alert("HELLO!");
public/css/styles.css:
header {
background-color: blue;
}
main {
background-color: orange;
}
footer {
background-color: teal;
}Add to views/partials/head.ejs:
<link rel="stylesheet" href="/css/styles.css">
Add to views/partials/footer.ejs:
<link rel="stylesheet" href="/js/scripts.css">
From command line run
nodemon app.js
and openlocalhost:3000
in a browser. After getting and closing the HELLO! alert, should see:
Add 404
Better add this in now so it’s not an afterthought.
- Create
views/404.ejs
file:<% include partials/head %>
<% include partials/header %>
<h2>404 Error</h2>
<h3>This page does not exist</h3>
<% include partials/footer %> - Add 404 catch-all to
/app.js
file. Note this must remain at the bottom, even when more routing files are added later.app.use(function(req, res) {
res.render("404");
});
Add Environment Variables Manager
To keep some things secret from the public when code is published to GitHub, environment variables can be used, and dotenv
is an npm package that helps manage these visibly.
Install the
dotenv
package as a dev dependency:npm install dotenv --save
Create a file called
.env
in the project’s root directory, and add all of the secret info as key-value pairs:PORT=5000
NODE_ENV=development
MONGODB_URI=mongodb://localhost/secret-app-address
SESSION_SECRET=seCret-p@s5w0rdIf other people will be collaborating on the project, also create a file called
.env.default
in the project’s root directory, and copy/paste from the.env
file, omitting the values:PORT=
NODE_ENV=
MONGODB_URI=
SESSION_SECRET=Make sure your collaborators get the secret sauce via secure channels so that they can replicate your dev environment on their own local machines.
SUPER IMPORTANT! Add the new
.env
file to.gitignore
Make the app use the
.env
file by invoking it inapp.js
. I usually do this conditionally by creating a dev environment, and using the environment files here only; later when deploying the app to a remote server, new values can be assigned to each key specifically for the production environment:var nodeEnv = process.env.NODE_ENV || "development";
if (nodeEnv === "development") {
require("dotenv").config()}
Link MongoDB Database
- Add Mongoose to the project, this will be the ORM to manage MongoDB from Node:
npm install mongoose --save
- In the
/app.js
file, require mongoose and connect to the database server. Also include the database model(s) which will be created in the next step:var mongoose = require("mongoose");
var TestItem = require("./models/test");
mongoose.connect(process.env.MONGODB_URI, {useMongoClient: true}); - Create a directory for the Mongoose models and add the file referenced in the previous step:
mkdir models
touch models/test.js - Create a database model in the newly created file
models/test.js
:var mongoose = require("mongoose");
// Test Schema & Model Setup
var testItemSchema = new mongoose.Schema({
name: String,
age: Number
});
module.exports = mongoose.model("TestItem", testItemSchema); - To test everything is linked correctly, add a dummy db entry into the very bottom of the
app.js
file:Look for this console message on the Node server. If it’s there, that’s all finished!// TEMPORARY DATABASE TEST -- DELETE WHEN REAL DATABASE MODELS ARE READY
var george = new TestItem({
name: "george",
age: 47
});
george.save().then(() => console.log("DATABASE IS LINKED & SAVING DATA!"));
Deploy To Heroku
Since I broke everything and had to reinstall the Heroku CLI, let’s start at the beginning.
To install Heroku I used Homebrew:
brew install heroku/brew/heroku |
Then I followed the prompts to log in with my existing Heroku account.
But actually…this isn’t completely necessary because it’s also possible to do the deployment by setting Heroku to watch a GitHub repo and automatically build and deploy with each push to the repo. Once completed, you can see build and server logs in the web interface; that said, installing the Heroku CLI is helpful, because you can see the logs a bit quicker by running heroku logs
from the command line.
To enable automatic builds from GitHub, open the Deploy tab and follow the steps to authenticate GitHub access from this Heroku app. A bit farther down the page, click Enable Automatic Deploys to your chosen branch so that Heroku will run a new build with each code push. If you ever want to change which branch deploys it’s quite simple—just click Disable Automatic Deploys
, change the branch, and enable them again.
SO…back on the Heroku web dashboard, we need to add a MongoDB database. Open the Resources tab and then Find More Add-Ons. mLab offers small free MongoDB servers perfect for a demo app, so find this on the list and add it to the project. When this is done in this method, Heroku automatically adds the new MONGODB_URI
environment variable to this app, so as long as the process key has the same name for the local dev environment, the app will be able to get the correct production value and access the web-hosted MongoDB server.
Now’s a good time to add in any other environment variables that you may need (like API keys, etc.); from the Settings tab, click Reveal Config Vars to add or edit these.
Heroku is configured by default to run the npm start
script to start your app. Make sure this script exists in the root folder’s package.json
file. In this case it will point to app.js
:
"scripts": { |
Ans that’s it! Once you have a successful build, you can click the Open App button from the top of the Heroku dashboard and you should see the same demo app page that you loaded locally earlier.
The End
These steps set up the basic boilerplate for a MEN app. Obviously a lot more can be added. For now this is good though!