Build a Node.js Express REST API with MongoDB & Atlas cloud service

A step by step tutorial to create, connect, test and manage data into a Node.js express REST API using Express, MongoDB, Mongoose, MongoDB-Atlas and Postman.

Dr. Ahmad Moussa
10 min readAug 22, 2021

This tutorial will create a Node.js REST API within a MongoDB database registered into the Atlas cloud service.

(If you are new to Node.js, you can get a picture from my article)

Post collection: a simple database schema

Our database schema is straightforward: a Post collection (i.e., table in SQL language speaking) with two attributes: title and content representing the title and the content of a post, respectively.

This article is organized as follows:

  • (A) — we will start by initializing our API,
  • (B) — we will convert our API to an express server,
  • (C) — we will explain how to parse the incoming data using express,
  • (D) — we will handle routes using express again.
  • (E) — once the routes are defined, we will associate them to actions.
  • (F) & (G) — after launching our API, we will show you how to test the API’s services using Postman.
  • (H) — after that, we will set up MongoDB-Atlas,
  • (I) — we will create a MongoDB database and connect it to the API,
  • (J) — we will create the post’s collection and show you how to instantiate it in the API,
  • (K) — finally, we will give you the entire code.

At the end of this article, if you find it interesting, kindly follow me, it doesn’t take time, but it helps a lot. Thank you.

(A) — App initialization

1. Create a brand new project folder.
2.
Go inside the new folder and run the following command with the confirmation of all default settings. It will set up a new npm package:

npm init

A package.json file should be created in your project after running this command.
3. Create a starting file app.js in the root of your project.

(B) — Server Creation

Now we will create our server. We will create an Express application (i.e., Express app).

4. Install express:

npm install — save express

5. If you don’t want to restart your server manually after every change, install nodemon package as a development dependency:

npm install — save-dev nodemon

6. Add a start script in the scripts section into your package.json file. This script will start the app.js (which will contain the server) with nodemon:

./package.json
--------------
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon app.js"
}

7. Create a very simple server:

./app.js
--------
const express = require('express');//import express
const app = express();// create the express app
app.listen(3000);// listen to incoming requests on port 3000

Till now, we have created the simplest Express app offering a server listening to the incoming requests on port 3000 (the port number does not matter, feel free to choose any port you want).

(C) — Parsing JSON data

We will be working with incoming JSON data, so we expect our clients to communicate with our API by requests that contain JSON data. So we need to handle the request’s body content by using a body-parser. The current version of Express includes a body parser that we can use:

./app.js
--------
const express = require('express');
const app = express();
app.use(express.json());// to parse incoming json
app.listen(3000);

This server can not do anything since it does not contain any endpoints: a routes. Now let’s add some routes.

(D) — Routes creation

Express comes with a router handler that can be used to create a routing system: express.Router.

8. Create a routes folder.
9. Create a feed.js route file and create an instance of express’s router within.
10. Set up two routes:
— A get route, which will return a static list of posts.
— A post route, allowing users to create a new post.
11. Export the router.

./routes/feed.js
----------------
const express = require('express');
const router = express.Router();// create a router
router.get('/posts');// GET /feed/posts will be handled right now
router.post('/post');// POST /feed/post will be handled right now
module.exports = router;// export the router

12. To be able to reach these endpoints, we need to register the router in app.js. We will forward all incoming requests that start with ‘/feed’ (feel free to choose your own route head) to the router:

./app.js
---------
const express = require('express');
const feedRoutes = require('./routes/feed');
const app = express();
app.use(express.json());
app.use('/feed', feedRoutes);
app.listen(3000);

At this stage, we have a server turning on port 3000 (http://localhost:3000/), with 2 accessible endpoints:
http://localhost:3000/feed/posts
http://localhost:3000/feed/post

But, no actions will be generated by accessing these endpoints. So let’s define the handler functions that should be executed when a request reaches the routes: the controller.

(E) — Controller-Handler creation

13. Create a controllers folder, and create in it a feed.js file.
14. Add two handler functions into the controller:
getPosts(): a function that returns a static post list.
createPost(): a function that receives a request to create a new post, and then it returns a confirmation message within the created post. The request body should contain two attributes for the post’s title and content.

Note that we use the current date as the ID for the post to be created (but this ID with the date should be deleted later because Mongoose will require charges on the ID).

./controllers/feed.js
---------------------
exports.getPosts = (req, res, next) => {
// return an array of posts
res.status(200).json({
posts: [
{
title: 'First Post',
content: 'This is the first post!'
}
]
})
};

exports.createPost = (req, res, next) => {
// get post's title and content from the request
const title = req.body.title;
const content = req.body.content;
// create a post with a dynamic Id with the current date
// return a confirmation message with the created post object
res.status(201).json({
message: 'Post created successfully!',
post: {
id: new Date().toISOString(),
title: title,
content: content
}
});
}

15. Associate handlers functions to the routes:

./routes/feed.js
----------------
const express = require('express');
const feedController = require('../controllers/feed');
const router = express.Router();
router.get('/posts', feedController.getPosts);
router.post('/post', feedController.createPost);
module.exports = router;

With that, we have some logic in place to return some dummy data. So let’s see if our application works now.

(F) — Launching of the server

16. Go inside your root project folder and run the command:

npm start

Your API is now ready to give you the defined services. Let’s check if these services work fine by testing them!

(G) — Testing your Node.js API

Our API contains just 2 possible endpoints:
/feed/posts => available via a GET request to http://localhost:3000/feed/posts

/feed/post => available via a POST request to http://localhost:3000/feed/posts

The GET request is easy to test, simply open your browser and enter the URL http://localhost:3000/feed/posts, and then you should get some JSON data.

This manual testing method can not be used to test POST requests, since we need to send some data within the request.

To handle such a case, we can use the Postman free API development tool. Go ahead and install it; once it’s installed, launch it.

In the main interface:
1. Go into the New tab section, or click on the plus icon button (+) to open a new tab.
2. Choose the POST verb.
3. Enter the URL (http://localhost:3000/feed/post)
4. In the Headers section, add a pair key & value for the content-type:
— Key: Content-Type
— Value: application/json
5. In the body section, check the row option and choose JSON (application/json)
6. In the body’s content, add a JavaScript object that contains the title & content attributes of the post you want to create.
7. Finally, click ‘send’ to send the request.

If everything is okay, in other terms, if your request has succeeded in creating a new post, you will see a confirmation message with the created post in the response body section. If so, CONGRATS!

Till now, we haven’t saved the created post anywhere in our application, so it will be lost once the server is turned off since it isn’t saved into an existing database system.

In this tutorial, we use a NoSQL database management system: MongoDB.

The choice of the database’s type (SQL Vs. NoSQL) is out of the scope of this article, but we will delve into this discussion in the upcoming articles, so stay tuned! ;)

To create the database, we will use MongoDB-Atlas which offers an on-demand fully managed service, so no need to install any extra software!

(H) — Setting up MongoDB-Atlas

We will create a cluster. The cluster will contain the set of our databases.
1. Go to https://cloud.mongodb.com/ and create an account.
2. Log into your account and click on ‘Create new cluster’.
3. Keep the default setting for the ‘Cloud provider’.
4. Choose the free option (M0) into the ‘Cluster Tier’.
5. Keep the default setting into ‘Additional’.
6. Feel free to choose a name for your cluster under ‘Cluster Name’.
7. Finally, click ‘Create cluster’.

Now we need to add a user to our database and give it permission to access.

8. Go to the ‘Security’ section, and click on ‘Add new database user’.
9. By default, the IP of your machine will be automatically added. If this is not the case, go to the IP Address list, click on ‘Add IP Address’, and add your IP address (or any IP if you want to give access permission for an external user).

(I) — Database creation & connection

Let’s now connect our application to the MongoDB database.

This step is divided into two main steps:

— connecting the API to Atlas (i.e., to the cluster), and,

— connecting the API to the database.

(I-a) — API-Cluster connection

1. Go to the created cluster, and click on ‘Connect’.
2. Choose your connection method. In our case, we will choose ‘Connect your application’.
3. Copy the given connection path:

'mongodb+srv://<user>:<password>@<cluster_name>.tw6br.mongodb.net/<database_name>?retryWrites=true&w=majority'

(I-b) — API-MongoDB connection

We will connect to MongoDB through Mongoose. Mongoose is an ODM (Object-Document Mapping Library). It allows us to define models within which we will work and where the queries are done behind the scenes.

1. Install Mongoose by running this command:

npm install — save mongoose

2. Create the connection into the app.js file.
Don’t forget to change the <user>, <password>, <cluster_name>, and <database_name> with your own values into the copied connection string.

./app.js
--------
// setup a database connection using mongoose
// paste the connection string given from your atlas server
mongoose
.connect( 'mongodb+srv://admin:root@medium-cluster.tw6br.mongodb.net/feeds?retryWrites=true&w=majority'
)
.then(result => {
app.listen(3000);
})
.catch(err => console.log('err', err))

3. Now we will create the models to interact with the database. So create a models folder with a post.js file to define what a post should look like.

./models/post.js
----------------
const mongoose = require('mongoose');// import mongoose// extract the schema from that mongoose object
const Schema = mongoose.Schema;
// create a new post schema
const postSchema = new Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
}
});
// export the model
module.exports = mongoose.model('Post', postSchema);

Although we have connected our API within the cluster, and although we have created the database’s schema, if we go to our Atlas and refresh, we won’t see any changes!

(J) — Database collection creation & instantiation

Using the model to create real Post records in the database
1. Go to your controller, under the createPost() function create a new instance of Post, and save it.
2. Go to Postman, and re-send the post request (see section G).
3. Go to Atlas, and refresh the databases list; you should see a newly created database (feeds) with one new collection (Post) within one document (the created instance of Post).

./controllers/post.js
---------------------
exports.createPost = (req, res, next) => {
const title = req.body.title;
const content = req.body.content;

// create a new Post instance
const post = new Post({
title: title,
content: content
});

// save the instance to the database
post
.save()
.then(postSaved => {
res.status(201).json({
message: 'Post created successfully!',
post: postSaved
});
})
.catch(err => console.log('err', err));
}

4. One last change to be done before we finish (and we celebrate our API :p ) is to modify the getPosts() function to be able to get back the existing posts in the database:

./controllers/post.js
---------------------
exports.getPosts = (req, res, next) => {
// return array of existing posts
Post.find().then(foundPosts => {
res.json({
message: "All posts",
posts: foundPosts
});
});
}

And there you go, we have created a node.js REST API giving two services and connected to a MongoDB database.

(K) — Full code snippet

The entire project's architecture
package.json
------------
{
"name": "assignment_6",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"mongoose": "^5.13.7"
},
"devDependencies": {
"nodemon": "^2.0.12"
}
}
app.js
------
//import express
const express = require('express');
// import mangoose
const mongoose = require('mongoose');
// import the feed routes
const feedRoutes = require('./routes/feed');
// create the express app
const app = express();
// to parse incoming json
app.use(express.json());
// forward any incoming request that starts with '/feed' to feedRoutes
app.use('/feed', feedRoutes);
// setup a database connection using mongoose
// past the connection string given from your atlas server
mongoose
.connect(
'mongodb+srv://admin:root@medium-cluster.tw6br.mongodb.net/feeds?retryWrites=true&w=majority'
)
.then(result => {
// listen to incoming requests on port 8080
app.listen(3000);
})
.catch(err => console.log('err', err))
routes/feed.js
---------------
// import express
const express = require('express');
// import the logic controller
const feedController = require('../controllers/feed');
// create a router
const router = express.Router();
// define your routes
// add the function that should be executed for this route
// GET /feed/posts will be handled right now
router.get('/posts', feedController.getPosts);
// POST /feed/post will be handled right now
router.post('/post', feedController.createPost);
// export the router
module.exports = router;
controllers/feed.js
-------------------
exports.getPosts = (req, res, next) => {
// return array of existing posts
Post.find().then(foundPosts => {
res.json({
message: "All posts",
posts: foundPosts
});
});
}
exports.createPost = (req, res, next) => {
const title = req.body.title;
const content = req.body.content;

// create a new Post instance
const post = new Post({
title: title,
content: content
});

// save the instance to the database
post
.save()
.then(postSaved => {
res.status(201).json({
message: 'Post created successfully!',
post: postSaved
});
})
.catch(err => console.log('err', err));
}
models/post.js
--------------
// import mongoose
const mongoose = require('mongoose');
// extract the schema from that mongoose object
const Schema = mongoose.Schema;
// create a new post schema
const postSchema = new Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
}
});
// export the model
module.exports = mongoose.model('Post', postSchema);

Did this article help you? If so, follow me for more similar content :)

--

--