Create an API with Express/NodeJS and MongoDB
August 28, 2018
The goal of this project is to teach you how to set up a very basic REST API using Express and MongoDB. You’ll work with the following tools in this tutorial:
- Express web framework for NodeJS
- MongoDB instance hosted on mLab
- Postman for verifying that your API actually works.
We will download/install these tools as we go through the tutorial.
Set up the Node environment
The first step is to make sure you have NodeJS installed. If not, you can download and install NodeJS from here: https://nodejs.org/en/download/
In your terminal, we will start with setting up your Node project. Make a new directory (let’s call it student_api
) and run npm init
in it.
mkdir student_api
cd student_api
npm init
The NodeJS project is now set up. Next we will install Express, MongoDB, and a library called body-parser
. body-parser
helps parse JSON requests that we’ll be dealing with. So, run the following command in your student_api
directory:
npm install --save express mongodb@2.2.16 body-parser
Next, we’ll install dev tool called nodemon
— this tool automatically restarts our NodeJS server whenever it observes any changes in our source files.
npm install --save-dev nodemon
At this point, it’s a good idea to update package.json file with the following line:
"scripts": {
"dev": "nodemon server.js"
},
You can think of package.json
as a configuration file for your entire NodeJS project. It contains all the dependencies/libraries we installed (like express, body-parser, mongodb etc.), as well as a few custom scripts that we may need to run. For instance, adding the line above lets us run the development script as npm run dev
. Here dev
is the name of the script, and it effectively executes the command nodemon server.js
. We will use this in a bit once we actually set up the server.js
file in the next step. Also, note that another file, called package-lock.json
got created automatically — this is a helper file to package.json
and stores the order in which the packages should be installed. We won’t need to deal with it at all in most cases — just make sure you don’t remove it.
Setting up the Server file
Moving on, let’s start setting up our server. We will start off by importing all our dependencies in a new file called server.js
:
// student_api/server.js
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const app = express();
const port = 8080;
app.listen(port, () => {
console.log('The server is now running on port ' + port);
});
We use MongoClient
to interact with the database. Our app
is an instance of express
, the web framework for Node that we are using to create this API. We’ve imported bodyParser
for later use in this file. Next we’ve defined a variable port
to be 8080
and get the express framework instance app
to listen to it and log out a statement so that we know it’s working.
Like we mentioned in the last step, now that server.js is set up, we can run npm run dev
to start server.js
via nodemon
. As soon as you run npm run dev, you’ll see the above statement logged out to the console.
Download and Install Postman
At this point, download and install Postman. Postman is a very small app that lets you simulate network requests as a real web client would. You can think of it as a specialized browser, but the interface is what your apps sees, not what your users see! The idea is that we’ll access the routes for Creating, Reading, Updating and Deleting the student records at localhost:8080/student/[:id] using Postman.
When Postman launches, just dismiss the initial menu and open a new tab using File → New Tab.
Set up the directory structure
This step would help us stay organized as we write larger apps. We will set up a directory called apps
, and a sub-directory called routes
inside it. Next we’ll create two files called index.js
and student_routes.js
inside this routes directory. You can execute all four steps in one go using the following commands:
mkdir app
cd app
mkdir routes
cd routes
touch index.js
touch student_routes.js
Once these files are set up, our directory structure should look like this:
Next, since routes in Express are wrapped in a function with which take the Express instance app as the first argument and the database db as the second, let’s write the following function where we will define our routes very soon:
// app/routes/student_routes.js
module.exports = function(app, db) {
};
Next, we will export this function through the app/routes/index.js file:
// app/routes/index.js
const studentRoutes = require('./student_routes');
module.exports = function(app, db) {
studentRoutes(app, db);
// Other route groups would go here when we have larger apps in the future
};
Finally, we’ll import these in server.js as follows:
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const app = express();
const port = 8080;
require('./app/routes')(app, {});
app.listen(port, () => {
console.log('The server is now running on port ' + port);
});
Notice that the second argument is just an empty object {} since we haven’t set up the database yet. Let’s do it in the next step.
Set up a MongoDB instance via mLab
mLab is a great tool to set up a real MongoDB instance in production. We will use the lowest free tier for this API. First, sign up for an mLab account as usual. Next create a MongoDB deployment. Choose SANDBOX as the plan type:
In the next step, choose the AWS region closest to you, and in the final step, set the database name as student
Next, click on the newly created Database, select the Users tab (the second tab) and click the “Add database user” button. Pick a Database username and password. Make sure the read-only box stays unchecked — we’ll be using this user to create/update/delete records as well! Lastly, note that this username/password is different from your actual mLab account — this is just to access your database, and should be kept secret. Don’t commit this username/password to public Github repositories, otherwise anyone would be able to access/modify your database.
Moving along, note the url on top to connect to the database:
We’ll be storing this second url, after replacing <dbuser>
and
cd ...
mkdir config
cd config
touch db.js
The first cd ...
just ensures you are back in student_api
directory.
Inside this db.js file, add the following lines:
module.exports = {
url : "YOUR URL HERE"
};
To make sure you never commit this file, add config/db.js
to your .gitignore
file — this will make sure that you’ll never commit this file and end up leaking your dbusername/dbpassword that was part of the url.
Next, in your server.js, use the MongoClient to connect to the database. Your server.js should look like this:
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const app = express();
const port = 8080;
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
require('./app/routes')(app, database);
app.listen(port, () => {
console.log('We are live on ' + port);
});
})
This is great so far! Our project is well set up, and now we will actually write the four routes — that should be fairly quick!
The Create Route
Let’s go back to the note_routes.js file. We’ll add our create route:
// student_routes.js
module.exports = function(app, db) {
app.post('/students', (req, res) => {
// You'll create the student record here.
console.log(req.body)
res.send('Hello')
});
};
When the Express instance (app
) gets a POST
request on /students
path, the code inside the block above gets executed. The request object req
has the request parameters, and the response object res
is used to respond to the request. In Postman, let’s add some x-www-form-urlencoded
data in the body tab of the request. Your screen should look as follows:
Once you click send, you would expect the body to get logged out along with the Hello returned to Postman. While the Hello comes back, the body log on the console says undefined
. This is where the body-parser
package comes in. Let’s add this line to server.js right after defining the port. We need this package since express cannot process URL encoded forms on its own.
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const app = express();
const port = 8080;
app.use(bodyParser.urlencoded({ extended: true }));
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
require('./app/routes')(app, database);
app.listen(port, () => {
console.log('We are live on ' + port);
});
})
When you hit Send on Postman again, you should see the req.body get printed in the terminal console this time.
Great, now that we have this information, we can use this to actually create a record in MongoDB. This part is easy. We will have a Mongo collection called students
, into which we will insert the student whose data is coming in via the POST request. Creating a note is as simple as calling insert on the collection:
//student_routes.js
module.exports = function(app, db) {
app.post('/students', (req, res) => {
const student = {
RollNo: req.body.RollNo,
SName: req.body.SName,
CollegeName: req.body.CollegeName,
Mob: req.body.Mob,
PercentageMarks: req.body.PercentageMarks
};
db.collection('students').insert(student, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occurred' });
} else {
res.send(result.ops[0]);
}
});
});
};
Try sending the request via Postman again. You should see the following output:
In mLab, you should see the record created on the MongoDB as well:
The Read Route
Suppose you wanted to get the details of the student you just created with the id. You want to access the student via localhost:8080/students/:id
. All we need to do is add another route in our student_routes.js
file:
app.get('/students/:id', (req, res) => {
const details = { '_id': <ID GOES HERE> };
db.collection('students').findOne(details, (err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(item);
}
});
});
We listen for a GET request at /students/:id, find one record with the given details (the id), and return the same. Here, we need to note that MongoDB requires not just the ID string, but the ObjectID. Just import ObjectID from mongodb and you should be good to go. Here’s what student_routes.js should look like now:
var ObjectID = require('mongodb').ObjectID;
module.exports = function(app, db) {
app.get('/students/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
db.collection('students').findOne(details, (err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(item);
}
});
});
app.post('/students', (req, res) => {
const student = {
RollNo: req.body.RollNo,
SName: req.body.SName,
CollegeName: req.body.CollegeName,
Mob: req.body.Mob,
PercentageMarks: req.body.PercentageMarks
};
db.collection('students').insert(student, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occurred' });
} else {
res.send(result.ops[0]);
}
});
});
};
The Update Route
The update route is very similar to get and create — it’s almost a fusion of the two:
app.put('/students/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
const student = {
RollNo: req.body.RollNo,
SName: req.body.SName,
CollegeName: req.body.CollegeName,
Mob: req.body.Mob,
PercentageMarks: req.body.PercentageMarks
};
db.collection('students').update(details, student, (err, result) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(student);
}
});
});
The Delete Route
Finally, the Delete Route:
app.delete('/students/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
db.collection('students').remove(details, (err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send('Student ' + id + ' has been deleted!');
}
});
});
Conclusion
This is it. You have now created an API to store data of students in a MongoDB database on mLab, using Express! Congratulations! If you want to expose your API to the external world (since it’s running on your localhost), you can use a tool like ngrok. Ideally, of course, you would want to deploy this API to production, instead of serving it from your own computer.