This article will outline how to build a simple app that records user input data to MongoDB and uses this data to generate a chart. This app will display employee names and ages in a bar chart. Let’s begin!
You can check out the (heavily commented) live server code here if you don’t want to read this post.
This article requires that you are already connected to MongoDB, have a collection named employees
, and have documents saved with name
(string) and age
(int 32) fields. I use a hosted MongoDB instance on MLab for this post.
The app is deployed here. I disabled the POST request functionality for the live deployed version for obvious reasons. So if you try to POST and it doesn't work, that's what happened.
Introduction & Table of Contents
A really popular technology stack includes using Express, MongoDB, & Node to create web applications. We know that at least a portion of our users implement these technologies to build dashboard views in many different applications, so we want to help people get started on the basic concepts. This tutorial will include:
-
- Installing & Initializing Express
- GET Request
- bodyParser
- POST Request
-
- Connecting to MongoDB
- Closing connection to MongoDB
- Saving data to MongoDB
- Querying data from MongoDB
-
- Setting up views folder & view engine
- Index
- Add Article
- API Endpoint
- Form
-
Fetching Data & Rendering A (Zing)Chart
- Fetching data
- Chart render
File Structure
mongodb-app
├───node_modules
├───views
│───api
│───data.ejs
│───employees
│───add.ejs
├───app.js
├───package.json
Express
In this tutorial, we will mainly be using Node as an environment to run JavaScript on the server. We will use Express as a back-end framework to execute server logic and execute HTTP (GET & POST) requests.
To get started, we need to make a folder for our project. Place this wherever is easiest for you. I saved mine onto my desktop.
mkdir mongo-app
Then, we will need to initialize our application and the package.json
file. Just push enter through all the options; we don't really need them for this example.
npm init
Next, we need to install Express via NPM in the terminal.
npm install express --save
We also need to include Express in our file and initialize it so we can start handling HTTP requests. All of our server logic will be created within the app.js
file. app.js
will be created within the mongo-app
directory. We can add Express to the top of app.js
like so:
const express = require(‘express’);
const app = express();
We also need to set up a port so we can run this app in our browser. We can use the .listen();
method on the app variable we just created to do this. This method takes in two arguments; the port we would like to use and an optional callback function. This will be placed at the bottom of our app.js
file.
app.listen(3000, () => console.log(‘listening to port 3000’);
GET
Express makes it easy to handle GET requests. All we need to do is use the app.get();
method on the app variable.
app.get();
takes in a callback function with two arguments:
-
request (req): Information about the GET request being made to Express.
-
response (res): The response we would like to send back to the user.
Our employee record app will need three GET request routes:
- Homepage
(‘/’)
- Add employee page
(‘employees/add’)
- API endpoint page
(‘api/data’)
Below will be the skeleton of our GET request routes. There will be more functionality added in later.
app.get(‘/’, function(req, res) => {
// do something
};
app.get(‘/employees/add’, function(req, res) => {
// do something
};
app.get(‘/api/data’, function(req, res) => {
// do something
};
Body-Parser
Before we can move onto handling POST requests in Express, we should cover a very useful Node Package called Body-Parser.
First, we need to download the package from NPM.
npm install body-parser --save
Then, we need to include bodyParser in our application. This will go in our app.js
file right under our Express variable values.
const bodyParser = require('body-parser');
We don’t really need to go too deep into detail about how Body-Parser works. You just need to know that Body-Parser is a middleware that parses the request (req)
object we are returned from POST requests to JSON.
Body-Parser provides all the functionality for us. All we need to do is copy and paste the code below into our app. This should allow us to use Body-Parser's functionality automatically.
app.use(bodyParser.urlencoded({ extended: false });
app.use(bodyParser.json());
POST
Post requests are handled almost identically to GET requests in Express. We can add POST requests using the app.post();
method. This method takes in a callback function with two arguments:
-
request (req): Information about the POST request being made to Express.
-
response (res): The response we would like to send back to the user.
We will only need to create one POST request route, which will be for the /employees/add
page. This is where new users will be submitted to our application. This will also go within our app.js
file.
Below is the POST request skeleton. We will add all the functionality in a later section of this tutorial.
app.post(‘employees/add’, function (req,res) {
// do database stuff
};
MongoDB
MongoDB provides a Node.js driver. This driver will allow us to write database operations and interact with our database using JavaScript. To use this driver, we need to install from NPM.
npm install mongodb --save
We can access the functionality of the MongoDB driver by requiring it within our app.js
file.
const mongodb = require(‘mongodb’).MongoClient;
We will connect to MongoDB on three separate occasions:
- When a user submits a GET request to
'/'
- When a user submits a POST request to
'/employees/add'
- When a user submits a GET request/fetches data from
'/api/data'
We also need to create a db variable value that stores the name of the database we are working in:
const dbName = 'employees';
Now we can move on to connecting to MongoDB and performing basic CRUD operations.
Connecting To MongoDB
We can connect to our MongoDB instance using the .connect();
method on our 'MongoClient' variable. This method will return a promise, which will take in the client
object as an argument. We can chain the .catch()
method to catch any errors in connecting to the database. The skeleton should look like this:
// opens connection to mongodb
MongoClient.connect(url).then(client => {
// checks for error in connecting to mongodb
}).catch(err => {
// logs message upon error connecting to mongodb
console.log('error connecting to mongodb', err);
});
Closing Connection To MongoDB
It is important to close connection to MongoDB once we are finished interacting with it. This is easy to achieve, but important to do. When we connect to MongoDB, the client we are returned is the client
object. We can call the .close()
method on this object to close the connection to MongoDB.
// opens connection to mongodb
MongoClient.connect(url).then(client => {
// closes connection to mongodb
client.close();
// checks for error in connecting to mongodb
}).catch(err => {
// logs message upon error connecting to mongodb
console.log('error connecting to mongodb', err);
});
Saving Data To MongoDB
Earlier, we created an EJS template for the /employees/add
page that contained our POST request form. We then created the corresponding POST request route with Express.
We also covered the use of the bodyParser
module that we will use to process the POST request data we receive from our form.
In order to save this data to MongoDB, we have to execute four things within our POST request route for /employees/add
:
- Connect to MongoDB
- Save POST request data into an object
- Create variable for our database
- Use the
.insertOne();
method on the 'employees' collection
We previously covered how to create a POST route with Express and connecting to MongoDB. So this is how the skeleton should look:
// creates POST request route
app.post(‘/employees/add’, (req, res) => {
// opens connection to mongodb
MongoClient.connect(url).then(client => {
// closes connection to mongodb
client.close();
// checks for error in connecting to mongodb
}).catch(err => {
// logs message upon error connecting to mongodb
console.log('error connecting to mongodb', err);
});
});
Next, we need to save our POST request (req
) data to an object variable. We will call this object variable employee
since we are receiving employee data. We will populate this object with data from our fields, which will be Name and Age.
To access this data all we need to do is use JavaScript dot notation on the req
object (bodyParser gives us easy access data returned from req
object).
let employee = {};
employee,name = req.body.name;
employee.age = req.body.age;
Next, we need to create a database variable so we can access collections from our database. We can use this with a combination of the client
object we receive from the .connect();
method, the .db();
method, and the dbName
variable we defined earlier.
let db = client.db(dbName);
Once this is done, we can start calling MongoDB operations on this variable.
The MongoDB client comes with a method called insertOne();
. This method will take in one argument, which will be the employee
object variable we defined earlier.
The insertOne
method returns a promise. This promise will take in the document doc
object as an argument. This doc
object will hold the data we just inserted into MongoDB. We can chain the .catch()
method to this promise to ensure we catch errors in inserting a document to MongoDB.
// inserts ONE employee into 'employees' collection
col.insertOne(employee).then(doc => {
/* logs message upon inserting employee to 'employees'
collection */
console.log('employee inserted', doc);
// closes connection to mongodb and logs message
client.close(() => console.log('connection closed'));
/* checks for error in inserting employee to 'employees'
collection */
}).catch(err => {
/* logs message upon error in inserting employee to 'employees'
collection */
console.log('error inserting employee', err);
});
There will be two things we need to do once a record is saved:
- Provide an indicator that the record was saved
- Close the connection to the database
There are a million different ways we can indicate a document has been saved to the database. In this instance, we will redirect users to our '/'
page. This page contains a list and chart exhibiting all employees and their ages. This way, our user will see that the record has been saved successfully.
We will use the Express .redirect()
method on the response res
object we are returned from the callback function in app.post();
.
res.redirect('/');
We will close the connection just as we did earlier. Here is what the full POST request code should look like:
// creates POST request route for /employees/add page
app.post('/employees/add', (req, res) => {
// logs message with POST request data
console.log(req.body);
/* creates empty employee object/stores POST request data in
employee object */
let employee = {};
employee.name = req.body.name;
employee.age = req.body.age;
// opens connection to mongodb
MongoClient.connect(url).then(client => {
// creates const for our database
const db = client.db(dbName);
// creates const for 'employees' collection in database
const col = db.collection('employees');
// inserts ONE employee into 'employees' collection
col.insertOne(employee).then(doc => {
/* logs message upon inserting employee to 'employees'
collection */
console.log('employee inserted', doc);
// redirects user back to index page after POST req submit
res.redirect('/');
// closes connection to mongodb and logs message
client.close(() => console.log('connection closed'));
/* checks for error in inserting employee to 'employees'
collection */
}).catch(err => {
/* logs message upon error in inserting employee to 'employees'
collection */
console.log('error inserting employee', err);
});
// checks for error in connecting to mongodb
}).catch(err => {
// logs message upon error connecting to mongodb
console.log('error connecting to mongodb', err);
});
});
Querying Data From MongoDB
Creating dynamic applications includes querying a database for documents, then passing these documents to our page via a template engine. This sentence sounds scary, but I will show you how easy this is.
The first step involves connecting to MongoDB (which we just learned how to do) and querying an existing MongoDB collection.
When we saved documents to MongoDB, we defined a database variable (db
). We also have to define this same variable in order to query documents. We can use the .find({});
method on this database (db
) variable. This will find ALL documents in our collection.
Then we need to convert these documents to an array using the .toArray();
method. This will return a promise, which will take in docs
object as an argument. We can chain the .catch()
method to this promise to ensure there are no errors in finding documents from MongoDB.
db.collection(‘employees’).find({}).toArray.then(docs => {
// do something with data
});
Within the promised returned from the find({})
and toArray();
methods we will:
-
Log a message if we succeed in finding documents
-
Render the EJS template for
‘/’
and pass the docs object we received from the.find({})
method as data to our template
Here is what the code should look like:
// creates GET request route for index
app.get('/', (req, res) => {
// opens connection to mongodb
MongoClient.connect(url).then(client => {
// creates const for our database
const db = client.db(dbName);
// creates const for 'employees' collection in database
const col = db.collection('employees');
// finds ALL employees in 'employees' collection/converts to array
col.find({}).toArray().then(docs => {
// logs message upon finding collection
console.log('found employees for index');
// renders index ejs template and passes employees array as data
res.render('index', {
employees: docs
});
// closes connection to mongodb and logs message
client.close(() => console.log('connection closed'));
// checks for error in finding 'employees' collection
}).catch(err => {
// logs message upon error in finding 'employees' collection
console.log('error finding employees', err);
});
// checks for error in connecting to mongodb
}).catch(err => {
// logs message upon error connecting to mongodb
console.log('error connecting to mongodb', err);
});
});
We will also run a similar protocol to send JSON data to the 'api/data'
page. However, instead of rendering an EJS template using the .render()
method on the response object, we will just send the data to the page using the .send()
method on the response object. Our code will look almost identical besides that fact.
// creates GET request route for /api/data page
app.get('/api/data', (req, res) => {
// opens connection to mongodb
MongoClient.connect(url).then(client => {
// creates const for our database
const db = client.db(dbName);
// creates const for 'employees' collection in database
const col = db.collection('employees');
// finds ALL employees in 'employees' collection/converts to array
col.find({}).toArray().then(docs => {
// logs message upon finding 'employees' collection
console.log('found employees for api');
// sends/renders employees array to /api/data page
res.send(docs);
// closes connection to mongodb and logs message
client.close(() => console.log('connection closed'));
// checks for error finding 'employees' collection
}).catch(err => {
// logs message upon error finding 'employees' collection
console.log('unable to find employees for api', err);
});
// checks for error in connecting to mongodb
}).catch(err => {
// logs message upon error connecting to mongodb
console.log('error connecting to mongodb', err);
});
});
EJS
Template engines make it easy to dynamically insert data from our database into regular HTML pages.
In this tutorial, we will use a very popular template engine called EJS. We will use EJS to display data we pull from our database for two reasons:
- To exhibit dynamic data to the homepage
(index.ejs)
- To create an API endpoint page
(/api/data)
We need to install EJS from the terminal using NPM. However, we don't need to include it within our app.js
file.
npm install ejs --save
Setting Up Views Folder & View Engine
We can configure EJS using the app.set();
method. This method will:
- Define the folder we will place our EJS templates in
- Define the view engine we will use
// sets ejs views folder
app.set(‘views’, path.join(__dirname, ‘views’);
// sets view engine
app.set(‘view engine’, ‘ejs’);
Index
We previously rendered the template for the index page within the database connection and query. We also passed through data we received from querying MongoDB with the .find({});
method. We can use this data to create an unordered list of employees. This list will contain:
- A list of employees generated through EJS
- A chart that exhibits employees and ages generated through EJS
First, we need to create an unordered <ul>
list. Then, we can loop through data to exhibit employees in this list.
We can loop through this data with the forEach()
method. This will loop through each employee in the employees
data set. We can pass each employee to a list item with EJS syntax. This is what it will look like below:
<!-- EJS loop through employees returned from MongoDB -->
<ul>
<!-- inserts each employee name/age from MongoDB to list -->
<% employees.forEach(employee => { %>
<li><%= `Name: ${employee.name}, Age: ${employee.age}` %></li>
<% }); %>
</ul>
Add Article
Apart from a normal HTML skeleton (which I will skip over), this page is essentially just a single form. It is a simple form with two fields and a submit. Our form will have two vital pieces of information:
method="POST"
: Will issue a post request upon submit.action="/employees/add"
: Will specify the URL it will post to.
This post request was handling above with Express. The form should look like this:
<form method="POST" action="/employees/add">
<label for="name">Name:</label>
<br><br>
<input type="text" name="name">
<br><br>
<label for="age">Age:</label>
<br><br>
<input type="text" name="age">
<br><br>
<input type="submit" value="submit">
</form>
API Endpoint
Our API endpoint will not need an EJS template. All we needed to do was send data from our MongoDB query to the view. We took care of that in the database section above.
Fetching Data & Rendering A (Zing)Chart
I recently wrote a post on how to fetch data from an endpoint using the Fetch API. You can read about it here for a refresher. I will just post the code on how to fetch a chart and render data below:
// creates variable for url we want to fetch
const url = 'http://localhost:3000/api/data';
// fetch call to our /api/data page
fetch(url)
// creates promise to work with response from /api/data
.then(res => {
// throws error if there is a problem fetching page
if (!res.ok) {
// returns error with response text of error
throw new Error(res.statusText);
}
// returns data from /api/data page in json format to next promise
return res.json();
})
// creates promise with returned data from previous promise
.then(data => {
// creates employees variable to store JSON data form /api/data
let employees = data;
// creates empty employeeInfo array
let employeeInfo = [];
// loops through data from employee variable
employees.forEach(employee => {
// pushes values from employees variable to empty employeeInfo array
employeeInfo.push([employee.name, parseInt(employee.age)]);
});
// creates chart const with employeeInfo array
const chart = {
type: 'bar',
series: [
{
values: employeeInfo
}
]
};
// renders zingchart to the page
zingchart.render({
id: 'chart',
data: chart,
height: '100%',
width: '100%'
});
// catches errors in promise chain
}).catch(error => console.log("fetch error"));
Conclusion
Now that we have finished the coding part, we need to run this app and test in our browser. All we need to do to run this file is type the following in our terminal:
node app.js
After that, you should be able to navigate to localhost:3000
in your browser and see a very basic chart displaying values from our database. You should also be able to navigate to localhost:3000/employees/add
and add an employee to our database.
Check out the full code below. Let us know if you have any questions. Comment below if you want to request any new tutorials. We are open to all suggestions.