Restful API with Node.js, Express, and MongoDB

In this tutorial, we learn about how to set up a simple restful API with Node.js, Express, and MongoDB.

Step 1: Install Node and NPM

Before we start, we need to have NPM and Node.js installed on our machine. We relying on NPM Node Package Manager to manage all the packages and modules for Node.js.

node -vnpm -v

Step 2: Create Node.js App

Once we have the npm installed, setting up a Node.js app can be very fast. Simply open a command prompt and run the commands below.

mkdir nodejs-api-tutorial // Create a project root directory
cd nodejd-api-tutorial
npm init

Step 3: Install Dependencies

Node.js has many modules to provide the functionality needed to build an API application. Run the command below to install the modules using NPM. It is quick to build a restful API with Node.js using NPM.

npm install express body-parser cors helmet mongoose app-config --save
  • Mongoose provides a straight-forward, schema-based solution to model your application data.
  • Helmet helps you secure your Express.js apps by setting various HTTP headers.
  • body-parser extract the entire body portion of an incoming request stream and exposes it on req.body
  • CORS allows or restricts requested resources on a web server depend on the HTTP request origin.
  • app-config helps you manage app configuration across multiple environments.

Step 4: Setup server.js Entry Point.

Copy the code below to your server.js file.

const express = require('express');
const bodyParser = require('body-parser');
const config = require('app-config');
const cors = require("cors");
const helmet = require("helmet");
const app = express();
const port = 5000;
// Optional: Configure cors to prevent unauthorised domain to access your resources
const allowlist = ['http://yourallowdomain.com'];
const corsOptionsDelegate = (req, callback) => {
let corsOptions;
let isDomainAllowed = allowlist.indexOf(req.header('Origin')) !== -1;
if (isDomainAllowed) {
// Enable CORS for this request
corsOptions = { origin: true }
} else {
// Disable CORS for this request
corsOptions = { origin: false }
}
callback(null, corsOptions)
}
app.use(cors(corsOptionsDelegate));// Optional: To add additional security to protect your HTTP headers in response.
app.use(helmet());
// parse requests of content-type - application/json
app.use(bodyParser.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
app.use('/', (req, res) => res.send('Hello World SGWebFreelancer'));// Any url that doesn't match will return as a 404
app.use(function(req, res, next) {
const err = new Error('Not Found');
res.status(404).send('Service Not Found 404');
err.status = 404;
next(err);
});
const server = app.listen(port, () => console.log('Server is up and running at port: ' + port));
npm start // this command will run node server.js

Step 5: Create MongoDB Connection

In this tutorial, we will be relying on the Mongoose module to insert and query our application data. It’s good practice to store our application and DB configuration variables in a dedicated file based on the environment. In this case, create a folder called config, and inside the directory, create another folder called dev. Before that, ensure you have MongoDB installed and started in your local machine.

const DBPORT = '27017';
const DBNAME = 'dbtutorial';
const DBUSER = 'user';
const DBPWD = 'password';
const DBHOST = '127.0.0.1';
const uri = 'mongodb://'+ DBUSER+':'+DBPWD+'@'+DBHOST+':'+DBPORT + '/'+DBNAME;
module.exports = {
'url' : uri
};
...
// import the mongoose library

const mongoose = require('mongoose');
...
// DB connection
mongoose.connect(config.db.url, // refer to the config/dev/db.js file
{
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true
}).then(()=> console.log('DB connected successfully.')
).catch(err => console.log('DB connection failed ' + err)
);
...
"scripts": {
"start": "NODE_ENV=dev node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
...

Step 6: Create Model and Controller

In this tutorial, we will use Model-View-Controller (or MVC) architectures to build the API. But since is a restful API, we will omit the view in this tutorial.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
productSchema = new Schema({
name: String,
description: String,
price: Number
},
{
timestamps: true // automatically manage createdAt and updatedAt properties
},
{
collection: 'Product'
});
const Product = mongoose.model('Products', productSchema);
const Models = { Product: Product };
module.exports = Models;
const Product = require('../models/product').Product;const products = {    // GET get all Products
getProducts: function(req, res) {
Product.find({}).then(data => {
res.send(data);
}).catch(err => {
res.status(500).send({
message: err.message || "Error occurred while retrieving products."
});
})
},
// GET Product by id
getProductsByID: function(req, res) {
const id = req.params.id;
Product.findById(id).then(data => {
if (!data)
res.status(404).send({
message: "Product with id " + id + " is not found."
});
else res.send(data);
}).catch(err => {
res.status(500).send({
message: err.message || "Error occured while retrieving Product with id " + id
})
})
},
// POST add new Product
addProduct: function(req, res) {
if (!req.body.name) {
res.status(400).send({
message: "Product name can not be empty!"
});
return;
}
const product = new Product({
name: req.body.name,
description: req.body.description,
price: req.body.price
})
product.save().then(data => {
res.send(data);
}).catch(err => {
res.status(500).send({
message: err.message || "Error occurred while creating the Product."
});
})
},
// PUT update Product by id
updateProduct: function(req, res) {
if (!req.body) {
return res.status(400).send({
message: "Data to update can not be empty!"
});
}
const id = req.params.id; Product.findByIdAndUpdate(id, req.body, {
useFindAndModify: false
})
.then(data => {
if (!data) {
res.status(404).send({
message: `Failed to update Product with id=${id}.`
});
} else res.send({
message: "Product was updated successfully."
});
})
.catch(err => {
res.status(500).send({
message: "Error occured while updating Product with id=" + id
});
});
},
// DELETE delete Product by id
deleteProduct: function(req, res) {
const id = req.params.id;
Product.deleteOne({
_id: id
})
.then(data => {
if (!data) {
res.status(404).send({
message: `Failed to delete Product with id=${id}.`
});
} else res.send({
message: "Product was deleted successfully."
});
})
.catch(err => {
res.status(500).send({
message: "Error occured while deleting Product with id=" + id
});
});
},
// DELETE remove all Products
deleteAllProducts: function(req, res) {
Product.deleteMany({})
.then(data => {
res.send({
message: "All Products was deleted successfully."
});
})
.catch(err => {
res.status(500).send({
message: "Error occured while deleting all Products"
});
});
},
}
module.exports = products;

Step 7: Create Routes

Create a routes/index.js file to map the HTTP request URL to the controller.

const express = require('express');
const router = new express.Router();
const product = require('../app/controllers/product.js')
router.get(`/api/products`, [product.getProducts]);
router.get(`/api/products/:id`, [product.getProductsByID]);
router.post(`/api/products`, [product.addProduct]);
router.put(`/api/products/:id`, [product.updateProduct]);
router.delete(`/api/products/:id`, [product.deleteProduct]);
router.delete(`/api/products`, [product.deleteAllProducts]);
module.exports = router;
...
//app.use('/', (req, res) => res.send('Hello World SGWebFreelancer'));
app.use('/', require('./routes'));

Additional

Window Environment

If you are using Windows, the command should look like this “set NODE_ENV=dev && node server.js”.

MongoDB

Run the command below to check if you have MongoDB installed in your local machine. If not, you can download it from here.

mongod --version
mongo> use dbtutorial // create db schema dbtutorial> db.createUser({user: "user", pwd: "password", roles:[]})  // create user> show users // to check user added successfully or not.

Nodemon

Nodemon is a tool that helps develop node.js applications by automatically restarting the node application when file changes in the directory are detected. This library is useful when doing development.

npm install nodemon --save"scripts": {
"start:dev": "NODE_ENV=dev nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},

Docker Compose

To run the system in production, it is easier to deploy using Docker.

# pull the Node.js Docker image
FROM node:alpine
# create the directory inside the container
WORKDIR /usr/src/app
# copy the package.json files from local machine to the workdir in container
COPY package*.json ./
# run npm install in our local machine
RUN npm install
# copy the generated modules and all other files to the container
COPY . .
# our app is running on port 5000 within the container, so need to expose it
EXPOSE 5000
# the command that starts our app
CMD ["node", "server.js"]
server {
listen 80;
server_name apiserver;
location / {
proxy_pass http://apiserver:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf
version: "3.3"
services:
apiserver:
build:
context: ./
ports:
- "5000:5000"
nginx:
restart: always
build:
context: ./nginx
ports:
- "80:80"
docker-compose up --build

SGWebfreelancer is a software developer with 6 years experiences.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store