Node.js article thumbnail image.

This guide will walk you through how to bootstrap a REST API using Node.js, Express.js, and MongoDB.

Our REST API will be built using Node.js and the Express.js framework. Express has a robust set of features that will help us build the REST API both quickly and effectively.

Also, we'll use MongoDB for a database and host it in the cloud using Atlas. Atlas will help us focus more on building our application than managing a database, which can be a real pain in the butt. And they have a free pricing tier you can use that is easily upgradeable if you ever needed to do so in the future.

In the first section, we'll bootstrap a new REST API application.

After that, we'll create a MongoDB database in the cloud and connect to it from the REST API application.

Then, we'll go over how to add, update, delete, and retrieve data from the database.

Let's get started!

Table Of Contents

Bootstrap An Express.js REST API Application

The first step is to get an Express REST API application up and running on your local development machine.

Let's get it built!

Create A New Express.js Application

Let's create a new parent folder for the REST API application.

On your local machine, create a new /rest-api directory with this command:

mkdir rest-api

Next, cd into your new /rest-api directory:

cd rest-api

Then, run the npm init -y command to create a package.json file and initiate a new Node.js application at the root of your project:

npm init -y

Then, create a new /rest-api/index.js file that will serve as the entry point to the REST API application:

touch index.js

And add this code to it via your editor:

const express = require("express")

const PORT = process.env.PORT || 5000

const app = express()

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

This code will start a server and listen on port 5000 for any requests. And it's also configured to log an Express app listening on port 5000 message when it first starts.

Make sure you save the file when you're done making changes.

Notice that we're using the external Express NPM package, so we need to install it using NPM:

npm install --save express

Now we need a way to run the code we just created.

To do that, we need to add a few scripts to the package.json file.

Open the /rest-api/package.json file in your editor and update the "scripts" section to the following:

"scripts": {
  "dev": "NODE_ENV=development nodemon index.js",
  "start": "NODE_ENV=production node index.js"
},

The first "dev" script will run the REST API application in development mode. And the "start" script will run the application in production mode, which you'll use when the application is deployed to a production environment.

The NODE_ENV variable will be available to the entire application via the process.env.NODE_ENV value. We'll use this to help determine what environment our code is running in.

At this point, your package.json file should look similar to this:

{
  "name": "rest-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "NODE_ENV=development nodemon index.js",
    "start": "NODE_ENV=production node index.js"
  },
  "keywords": [],
  "author": "git_username <your_email>",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  }
}

Also, you may have noticed that we use Nodemon when running the application in development mode.

This NPM package will monitor the application code for any changes and automatically restart the application if changes are detected. Without it, we would have to manually restart the application every time we wanted to make any code changes.

You can install the nodemon NPM package with this command:

npm install --save-dev nodemon

Now we can give the application a test run.

Make sure you're at the root of the /rest-api project and run this command:

npm run dev

If all went as planned, this should output an Express app listening on port 5000 message in your terminal. This indicates that the Express application is running and listening for any incoming requests.

Advertisement
Find Remote Coding Jobs
RemotelyWeCode.com curates the best remote software development jobs from startups and companies around the world.
Find Jobs

Install & Configure Helmet

We also want to add the Helmet NPM package to the application. It will make the Express application more secure and can be added to the application in one line of code.

The helmet NPM package is a collection of twelve smaller middleware functions that set HTTP headers for your REST API application. Running app.use(helmet()) will not include all of these middleware functions by default.

But, these are the ones that are applied by default:

  • DNS Prefetch Control: lets you disable the browser's DNS pre-fetching by setting the X-DNS-Prefetch-Control header.
  • Frameguard: stops click-jacking attacks by setting the X-Frame-Options header.
  • Hide Powered-By: removes the X-Powered-By header to make it harder for attackers to see what potentially vulnerable technology powers your site.
  • HSTS: sets the Strict-Transport-Security header to keep your users on HTTPS.
  • IE No Open: sets the X-Download-Options to prevent Internet Explorer from executing downloads in your site’s context.
  • Don't Sniff Mimetype: helps prevent browsers from trying to guess (a.k.a. "sniff") the MIME type, which can have security implications. It does this by setting the X-Content-Type-Options header to nosniff.
  • XSS Filter: sets the X-XSS-Protection header to prevent reflected XSS attacks.

Install the helmet NPM package with this command:

npm install --save helmet

Then, update the /rest-api/index.js file to both require() the package and add a app.use(helmet()) line of code to initiate it.

When you're done making edits, the /rest-api/index.js file should look like the following:

const express = require("express")
const helmet = require("helmet")

const PORT = process.env.PORT || 5000

const app = express()

app.use(helmet())

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Helmet should now be configured for your application.

Before moving on, verify that your application is still running without any errors.

Enable CORS Support

Cross-origin resource sharing (CORS) allows AJAX requests to skip the same-origin policy and access resources from remote hosts (i.e. websites).

Therefore, we need to configure CORS in the application and restrict AJAX access to only allow the origin of your website. This will allow your website to make requests to the REST API.

When using Express, the easiest way to configure CORS is by using the Cors NPM package. You can install it with this command:

npm install --save cors

Then, open your /rest-api/index.js file and make these two code changes:

  • require() the cors npm package (const cors = require("cors")).
  • Add an Express middleware that utilizes the cors NPM package (app.use(cors())).

When you're done making changes, the /rest-api/index.js file should look like the following:

const express = require("express")
const helmet = require("helmet")
const cors = require("cors")

const PORT = process.env.PORT || 5000

const app = express()

app.use(helmet())

app.use(cors())

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Now we need to configure the cors package to only allow cross-origin requests from the origin for your website. In development mode, you can allow requests from anywhere. But, in the production environment, it needs to match the origin of your website.

The cors() function we used also takes an object as a parameter that we can use to pass in some specific configuration options.

So, let's change the cors() middleware's code into the following:

app.use(cors({
  origin: process.env.NODE_ENV === "development" ? "*" : /domain\.com$/
}))

The origin option tells cors what domain or URL to allow requests from. For example, if you set it to http://example.com, only requests from http://example.com will be allowed.

In development mode, we set it to *. This will allow requests from any URL origin.

And in production mode, we'll use a regular expression (/domain\.com$/) to accept requests from a specific domain origin.

For your reference, the /rest-api/index.js file should now look like this:

const express = require("express")
const helmet = require("helmet")
const cors = require("cors")

const PORT = process.env.PORT || 5000

const app = express()

app.use(helmet())

app.use(cors({
  origin: process.env.NODE_ENV === "development" ? "*" : /domain\.com$/
}))

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Before moving on, verify that your application is still running without any errors.

Add Express Parser Middleware

The last thing we need to do is add the Body-Parser package to the application as Express middleware.

The Body-Parser package is required for us to "read" the body of any incoming JSON requests.

You can install it with this command:

npm install --save body-parser

Then, you need to require() the NPM package in the /rest-api/index.js file so it's available for use:

const bodyParser = require("body-parser")

After that, you need to add these two lines of code to create the Express middleware:

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

The first middleware (bodyParser.json()) will allow the REST API application to parse JSON data sent to the REST API. And the second middleware (bodyParser.urlencoded()) will allow your application to parse urlencoded request bodies.

When you're done making edits, the /rest-api/index.js file should look like this:

const express = require("express")
const helmet = require("helmet")
const cors = require("cors")
const bodyParser = require("body-parser")

const PORT = process.env.PORT || 5000

const app = express()

app.use(helmet())

app.use(cors({
  origin: process.env.NODE_ENV === "development" ? "*" : /domain\.com$/
}))

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Save the changes you made to the file and verify your application is still running without errors.

The REST API is now bootstrapped and ready to be built upon!

In the next section, we'll create and connect to a MongoDB database.

Advertisement
Build A Hacker News Clone
Learn how to build and deploy a full-stack, production-ready application with React.js, Next.js, Node.js, & MongoDB.
View Course

Create & Connect To A New MongoDB Database

For the database, we're going to use the Atlas service to create a MongoDB database in the cloud.

Atlas will allow us to quickly get a MongoDB database running in the cloud, will handle all the security and headache-inducing aspects of managing a database, and has a generous free-tier for you to use as well.

In this section of the course, we'll create the database and then connect to it via the REST API application. We'll also go over how to securely store your database credentials.

Let's get to it!

Create A MongoDB Database Using Atlas

First, you'll need to log into the Atlas website or create a free account if you haven't already.

After successfully logging in or signing up, navigate to the create a cluster page if they didn't already redirect you there. The page should look similar to the image below.

MongoDB Atlas Create a Cluster Page

To begin, choose the free cluster option. This will give you more than enough horsepower to start with.

This will redirect you to a page where you can choose some configuration settings for the server your database will run on.

The first option is the cloud provider and region, which is the location of the server housing your database. Choose the option that is in the closest geographic proximity to where your visitors will be physically located. The closer a request is to the server, the faster it will be fulfilled.

MongoDB Atlas Choose Server's Provider & Region

In the next section, you can choose from a set of cluster tiers with different storage and bandwidth capabilities. Ensure the free option is selected before proceeding.

MongoDB Atlas Choose Server's Cluster Tier

After that, you can skip the Additional Settings section.

Last, give your cluster a name that will be displayed in your account control panel.

MongoDB Atlas Give Cluster a Name

When you've gone through each section, hit the Create Cluster button to initiate the creation of your new database.

MongoDB Atlas Create Cluster Button

It will take a few minutes for Atlas to create the cluster in their system. When the process is complete, you should see your new database cluster on a page that looks like the image below.

MongoDB Atlas Database Cluster Dashboard Page

Before you can use the new database for anything, you'll need to first secure it by adding your IP address to the connection whitelist and then create a MongoDB username and password for the database.

Click on the Connect button in the cluster dashboard area. This will open a dialog box that will guide you through configuring some security settings for the database.

First, click on the Add Your Current IP Address button and verify that Atlas has populated the input field with the correct IP address matching your local development machine.

MongoDB Atlas Whitelist a Connect IP Address

If it's correct, click the Add IP Address button to add your IP address to the whitelist. This will allow you to successfully connect to the database.

MongoDB Atlas Whitelist a Connect IP Address

In the bottom section labeled Create a MongoDB User, enter a username and password for a new MongoDB database user. These are the credentials you'll use to read and write data to and from the database.

MongoDB Atlas Create a MongoDB User

When you've added a username and password, click on the Create MongoDB User button to generate the new user.

With both the Whitelist a connection IP address and Create a MongoDB User sections complete, you can click the Choose a connection method button to move on to the next step.

MongoDB Atlas Choose a Connection Method

There are three different ways to connect to your new database, including the Mongo Shell, using a MongoDB native driver alongside your favorite programming language, and using MongoDB's GUI called Compass.

Since we'll be connecting via the Node.js programming language, select the Connect your application option. When you click on that option, a page will be displayed where Atlas generates a connection string you can copy and paste into your application.

Make sure Node.js is selected as the coding language driver. In the second section, a connection string should be generated for you.

MongoDB Atlas Connect to Your Database Using Node.js

Copy that string to your clipboard. We'll use it to connect to your database.

Connect To The Database

Now we need to add some code to the REST API application to facilitate the interaction between the application and the MongoDB database we just created.

We'll use the Mongoose NPM package to help us manage our MongoDB data and schemas. Therefore, we need to install it into the project dependencies.

You can install the package with this command:

npm install --save mongoose

Now we can require() and use the mongoose NPM package in the /pages/index.js file of your application:

const mongoose = require("mongoose")

Make sure you have the connection string handy from the last section. It should look similar to this:

mongodb+srv://<username>:<password>@my-cluster.mjegg.mongodb.net/myFirstDatabase?retryWrites=true&w=majority

Re-open the /rest-api/index.js file in your code editor and add this code block to it (place it below the const app = express() variable):

const mongoString = "YOUR_CONNECTION_STRING"

mongoose.connect(mongoString)

mongoose.connection.on("error", function(error) {
  if (process.env.NODE_ENV === "development") {
    console.log(error)
  }
})

mongoose.connection.on("open", function() {
  console.log("Connected to MongoDB database.")
})

The mongoString variable stores the connection string to your MongoDB database. Make sure you use the connection string and username/password provided to you on the Atlas website.

Using that connection string, we connect to the MongoDB database using the mongoose.connect() method.

Then, we added some code to listen for both any errors that occur and the successful connection to the database. We use the mongoose.on() method to listen for those events.

If an error occurs after the initial connection is made, we want to console.log() the error so we understand what happened. And we also want to log a success message when the application initially connects to the database.

For your reference, the /rest-api/index.js file should look similar to this:

const express = require("express")
const helmet = require("helmet")
const cors = require("cors")
const bodyParser = require("body-parser")
const mongoose = require("mongoose")

const PORT = process.env.PORT || 5000

const app = express()

const mongoString = "YOUR_CONNECTION_STRING"

mongoose.connect(mongoString)

mongoose.connection.on("error", function(error) {
  if (process.env.NODE_ENV === "development") {
    console.log(error)
  }
})

mongoose.connection.on("open", function() {
  console.log("Connected to MongoDB database.")
})

app.use(helmet())

app.use(cors({
  origin: process.env.NODE_ENV === "development" ? "*" : /domain\.com$/
}))

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Save the /rest-api/index.js file and start the application again in your terminal (it should automatically reload if it was already running with Nodemon):

npm run dev

If everything went as planned, you should see two output messages in the terminal:

Express app listening on port 5000
Connected to MongoDB database.

If you see the Connected to MongoDB database. message, it means you've connected to the MongoDB database.

If you run into errors, verify that the code matches what we provided above and that your connection string username and password credentials match what you created on the Atlas website dashboard.

Also, verify that you have whitelisted the correct IP address. Otherwise, you're local development machine may be blocked from connecting.

Once you have everything connected and working, we can move on to the next step!

Advertisement
Build A Coding Blog From Scratch
A complete step-by-step guide to building and deploying a coding blog with a custom CMS using React.js, Node.js, MongoDB, & friends.
View Course

Store Database Credentials As Environment Variables

An additional thing you'll want to do is create a .env file for the application and use it to store your database credentials.

For instance, if you push your code to GitHub (or GitLab), you can add a .env declaration to your .gitignore file to exclude your credentials from being pushed with your code.

This process will greatly limit the chance of your credentials being accidentally revealed to others by controlling what environments they show up in (i.e. only on your local development or production server machines).

First, create a new .env file in the root of your /rest-api directory:

touch .env

Then, add these two environment variables to it:

DB_USERNAME=username
DB_PASSWORD=password

Make sure you add your actual credentials for both the DB_USERNAME and DB_PASSWORD values. And then save the file.

Now we need a way to load the variables from the .env file into the application so we can access them via process.env.DB_USERNAME or process.env.DB_PASSWORD in the code.

To make this easy, we'll use the Dotenv NPM package to facilitate the loading and use of .env variables in the code.

You can install it with this command:

npm install --save dotenv

To get this working in your code, we need to require() the NPM package into the /rest-api/index.js file:

const dotenv = require("dotenv")

And use the dotenv.config() line of code to initiate the package in the index.js file (place it directly below the package require() code):

dotenv.config()

Once you initiate the dotenv configuration file with dotenv.config(), the variables from your .env file will be available for use.

Now you can update your connection string to use the environment variables in the .env file.

Here's an example of what that looks like:

const mongoString = `mongodb+srv://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@my-cluster.mjegg.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`

We replace the username and password in your MongoDB connection string (const mongoString) with the process.env.DB_USERNAME and process.env.DB_PASSWORD variables. And we use ES6 template literals to embed the variables directly into the string.

For your reference, the entire /pages/index.js file should look like the following:

const express = require("express")
const helmet = require("helmet")
const cors = require("cors")
const bodyParser = require("body-parser")
const mongoose = require("mongoose")
const dotenv = require("dotenv")

dotenv.config()

const PORT = process.env.PORT || 5000

const app = express()

const mongoString = `mongodb+srv://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@my-cluster.mjegg.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`

mongoose.connect(mongoString)

mongoose.connection.on("error", function(error) {
  if (process.env.NODE_ENV === "development") {
    console.log(error)
  }
})

mongoose.connection.on("open", function() {
  console.log("Connected to MongoDB database.")
})

app.use(helmet())

app.use(cors({
  origin: process.env.NODE_ENV === "development" ? "*" : /domain\.com$/
}))

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Save the file when you're done editing and make sure the application is restarted. You shouldn't see any errors and it should be running in your terminal like it was before.

That was the last step to create and connect to your MongoDB database.

Add, Get, Update, & Delete Database Data

Now that we've bootstrapped a REST API application and connected it to a MongoDB database, let's go over how to interact with the database.

In this section we'll go over the following:

  1. Create a database model we'll use to interact with the database.
  2. Configure API routes for your application.
  3. Add data to the database.
  4. Retrieve data that has been saved to the database.
  5. Update data in the database.
  6. Delete data from the database.

Let's walk through each of those steps!

Create A Mongoose Database Model

Before we can interact with the database, we need to give Mongoose a model so it can read documents in the MongoDB database.

First, you'll need a new directory called /models to store all your database model files.

Execute this command to create the new directory in the root of your project folder:

mkdir models

The full path of the directory should be /rest-api/models.

Then, cd into the new directory:

cd models

As an example, we'll be adding data to the database pertaining to foods. Feel free to replace that data with whatever pertains to your application.

Create a new file in that directory named food.js:

touch food.js

Then, add this code to it:

const mongoose = require("mongoose")

const FoodSchema = new mongoose.Schema({
  name: {
    type: String,
    unique: true,
    required: true
  },
  calories: {
    type: Number
  },
  description: {
    type: String
  }
})

FoodSchema.index({name: 1}, {unique: true})

module.exports = mongoose.model("Food", FoodSchema)

First, we create what's called a Mongoose Schema with the new mongoose.Schema() class. Each key in the FoodSchema code defines a property in the documents that will be added to the MongoDB database:

  • name: the unique identifier given to each food. Duplicates of this will not be allowed in the database (via unique: true). The required: true option won't allow a document to be saved if it doesn't have a name property.
  • calories: the number of calories a given food has.
  • description: a short description of the food.

To use the schema definition, we need to convert the FoodSchema variable into a Mongoose Model we can work with.

To do that, we pass it into the mongoose.model("Food", FoodSchema) method. We then export the model so we can require() and use it in other files where we interact with the database.

We also define a MongoDB Index with the FoodSchema.index() method.

Without indexes, MongoDB must perform a collection scan, i.e. scan every document in a collection, to select those documents that match the query statement. If an appropriate index exists for a query, MongoDB can use the index to limit the number of documents it must inspect.

For the FoodSchema model, we'll index documents based on the name key. Therefore, when we query the database using that value, the query will be much faster and take less computing power.

Advertisement
Find Remote Node.js Coding Jobs
RemotelyWeCode.com is the leading job board for coders that work remotely. Check out our curated collection of Node.js jobs.
Find Jobs

Configure API Routes

Now that we have the food database model created, let's add a new /routes directory to your application that will store all the code for your API routes.

In your terminal, navigate to the root of the /rest-api project folder and create the new directory:

mkdir routes

The full path of the directory should be /rest-api/routes.

Then, cd into the new directory:

cd routes

To keep your route code organized, you should separate each route category (i.e. foods, users, etc.) into its own sub-directory.

So, let's create a directory that will store all the route code pertaining to food:

mkdir foods

And then cd into it:

cd foods

Inside that directory, we'll separate the code into two files:

  1. index.js: will handle the URL endpoints that the website will use to make requests to the REST API.
  2. api.js: will handle the direct interactions with the MongoDB database.

Create those two files inside the /routes/foods directory with this command:

touch index.js api.js

Then, let's add some starter code to each file to get things going.

First, open the index.js file and add this code to it:

const express = require("express")

const api = require("./api.js")

const app = express.Router()

// API ENDPOINT CODE WILL GO HERE

module.exports = app

This file is where we'll build all the individual routes for any user-related requests to the REST API.

The const app = express.Router() creates an Express instance that will be used as middleware in your application. When we start building the routes, we'll put them where the // API ENDPOINT CODE WILL GO HERE line of code is placed now.

The last thing we do is export the app Express middleware instance.

Next, let's add some initial code to the api.js file as well:

const FoodModel = require("../../models/food.js")

module.exports = {
  // FUNCTIONS WILL GO HERE
}

For now, we simply import the FoodModel we just built and create a module.exports object. Inside that object, we'll create functions that can be imported and used in other files (specifically the /routes/foods/index.js file in this case).

Also, each function will have a callback function as one of its parameters. This will ensure that our route code waits until the database query is finished before sending the response back to the browser or application that made the request.

The last thing we need to do is tell Express to use the API route file (/routes/foods/index.js) that we just created.

To do this, open the /rest-api/index.js file that serves as the entry point to the application. We need to add this line of code to the bottom of the file, but before the app.listen() statement:

app.use(require("./routes/foods/index.js"))

After that's been added, the entire file should look like this:

const express = require("express")
const helmet = require("helmet")
const cors = require("cors")
const bodyParser = require("body-parser")
const mongoose = require("mongoose")
const dotenv = require("dotenv")

dotenv.config()

const PORT = process.env.PORT || 5000

const app = express()

const mongoString = `mongodb+srv://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@my-cluster.mjegg.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`

mongoose.connect(mongoString)

mongoose.connection.on("error", function(error) {
  if (process.env.NODE_ENV === "development") {
    console.log(error)
  }
})

mongoose.connection.on("open", function() {
  console.log("Connected to MongoDB database.")
})

app.use(helmet())

app.use(cors({
  origin: process.env.NODE_ENV === "development" ? "*" : /domain\.com$/
}))

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

app.use(require("./routes/foods/index.js"))

app.listen(PORT, function () {
  console.log(`Express app listening on port ${PORT}`)
})

Now we can start interacting directly with the database.

Add Data To The Database

To start, let's create an API route that adds a new food document to the database.

When a new food is added to the database, a POST type request will be made to the "/foods/create-new-food" API endpoint. In that request, data for the food's name, calorie count, and description will be included.

Let's configure your REST API to handle those types of requests.

Open the /routes/foods/index.js file and add this code to it (replace // API ENDPOINT CODE WILL GO HERE):

app.post("/foods/create-new-food", function(req, res) {
  // next code block goes here

})

This code configures your application to take POST requests at the "/foods/create-new-food" URL endpoint.

The data related to the new food item will be stored in the request.body object sent along with the request.

Now we need to handle that data.

To do that, update the code to look like this:

app.post("/foods/create-new-food", function(request, response) {
  if (!request.body.name || !request.body.calories || !request.body.description) {
    response.json({error: true})
  } else {
    api.createNewFood(request.body.name, request.body.calories, request.body.description, function(result) {
      response.json(result)
    })
  }
})

Let's go over each part of that code.

First, we verify that the request contains all of the necessary data for the food's name, calories, and description. If one of those is missing, we return an error response using res.json().

If the request contains all the necessary data, we call a api.createNewFood() function that will be imported from /routes/foods/api.js file.

This function will add the new food to the database and return a response that we'll send back to the external website or application that made the request.

To make sure we're on the right track, the entire /routes/foods/index.js file should look like this:

const express = require("express")

const api = require("./api.js")

const app = express.Router()

app.post("/foods/create-new-food", function(request, response) {
  if (!request.body.name || !request.body.calories || !request.body.description) {
    response.json({error: true})
  } else {
    api.createNewFood(request.body.name, request.body.calories, request.body.description, function(result) {
      response.json(result)
    })
  }
})

module.exports = app

Let's get the api.createNewFood() function created.

Open the /routes/foods/api.js file and add this function to the module.exports object (replace // FUNCTIONS WILL GO HERE):

createNewFood: function(name, calories, description, callback) {
  // next code block goes here

}

When the createNewFood() function is called, it will take four parameters:

  • name of the food.
  • The number of calories the food has.
  • A short description of the food item.
  • A callback that's used to return an object when the function is finished.

Inside the function, we can now save the food to the database.

To do that, update the function's code to the following:

createNewFood: function(name, calories, description, callback) {
  const newFood = new FoodModel({
    name: name,
    calories: calories,
    description: description
  })

  newFood.save(function(error, newItem) {
    if (error) {
      callback({error: true})
    } else {
      callback({success: true})
    }
  })
}

In this code, we create a new database document using the new FoodModel({}) method.

In the new document, we include the name, calories, and description values passed to the function as parameters.

Then, we save the new food document to the database using the save() Mongoose method.

When the database update is complete, we use the callback() to end the function and return a success object.

If an error occurs, we send an error message back instead.

To make sure we're on the right track, the entire /routes/foods/api.js file should look like this:

const FoodModel = require("../../models/food.js")

module.exports = {
  createNewFood: function(name, calories, description, callback) {
    const newFood = new FoodModel({
      name: name,
      calories: calories,
      description: description
    })

    newFood.save(function(error, newItem) {
      if (error) {
        callback({error: true})
      } else {
        callback({success: true})
      }
    })
  }
}

The API route is now complete!

Before moving on, verify that your application is running without any errors.

To test this, we need to send a POST request to the "/foods/create-new-food" URL endpoint.

In the request body, the food item's name, calories count, and description need to be included.

You can do that using Postman:

Postman POST Request With JSON Data Screenshot

Or using curl:

curl -X POST -H  "Content-Type: application/json" -d '{"name": "Tacos", "calories": 200, "description": "Beef and cheese in a tortilla."}' http://localhost:5000/foods/create-new-food

If the request was made successfully, you should get a {success: true} response back from the REST API.

After you make the request, head over to your Atlas MongoDB database dashboard. You should see the new food document added to the database.

View POST Request Data In The MongoDB Atlas Dashboard
Advertisement
Build A Hacker News Clone
Learn how to build and deploy a full-stack, production-ready application with React.js, Next.js, Node.js, & MongoDB.
View Course

Get Database Data

Next, let's retrieve data from the database using a GET request.

Open the /routes/foods/index.js file and add this block of code to it:

app.get("/foods/get-all-foods", function(request, response) {
  api.getAllFoods(function(result) {
    response.json(result)
  })
})

This block of code configures your application to handle GET requests at the "/foods/get-all-foods" URL endpoint.

To retrieve the foods from the database, we'll use a api.getAllFoods() function. This is a function we'll build and import from the /routes/foods/api.js file.

For your reference, the entire /routes/foods/index.js file should look like this:

const express = require("express")

const api = require("./api.js")

const app = express.Router()

app.post("/foods/create-new-food", function(request, response) {

  . . .

})

app.get("/foods/get-all-foods", function(request, response) {
  api.getAllFoods(function(result) {
    response.json(result)
  })
})

module.exports = app

Let's get the api.getAllFoods() function built next.

Open the /routes/foods/api.js file and add this function to the module.exports object:

getAllFoods: function(callback) {
  FoodModel.find({}).lean().exec(function(error, foods) {
    if (error) {
      callback({error: true})
    } else {
      callback({success: true, foods: foods})
    }
  })
}

In this function, we use the find() MongoDB query to get all the foods currently stored in the database.

When the query is finished, the function will return the foods data using the callback().

The foods data will be in the form of an array of objects where each food item is an object.

For your reference, the /routes/foods/api.js file should look like this:

const FoodModel = require("../../models/food.js")

module.exports = {
  createNewFood: function(name, calories, description, callback) {

    . . .

  },
  getAllFoods: function(callback) {
    FoodModel.find({}).lean().exec(function(error, foods) {
      if (error) {
        callback({error: true})
      } else {
        callback({success: true, foods: foods})
      }
    })
  }
}

The API route is ready to test!

Here's what the Postman request would look like:

Postman GET Request Screenshot

And the curl request:

curl http://localhost:5000/foods/get-all-foods

The response from the REST API should contain an array of objects containing each food you've added to the database.

{"success":true, "foods":[{"_id":"6128b84df2fd9245e010f815","name":"Tacos","calories":200,"description":"Beef and cheese in a tortilla.","__v":0}]}

Update Database Data

Next, let's go over how you'd update a food item that's already been saved to the database.

As an example, let's create an endpoint used for changing a food's description.

Open the /routes/foods/index.js file and add this code block to it:

app.put("/foods/change-food-description", function(request, response) {
  if (!request.body.name || !request.body.newDescription) {
    response.json({error: true})
  } else {
    api.changeFoodDescription(request.body.name, request.body.newDescription, function(result) {
      response.json(result)
    })
  }
})

This code configures your REST API to take PUT requests at the "/foods/change-food-description" URL endpoint.

In the request body, there should be both a name and newDescription value.

The name value will be used to retrieve the correct food from the database. And the newDescription value will be saved to the database as the new description for the food.

For your reference, the entire /routes/foods/index.js file should look like this:

const express = require("express")

const api = require("./api.js")

const app = express.Router()

app.post("/foods/create-new-food", function(request, response) {

  . . .

})

app.get("/foods/get-all-foods", function(request, response) {

  . . .

})

app.put("/foods/change-food-description", function(request, response) {
  if (!request.body.name || !request.body.newDescription) {
    response.json({error: true})
  } else {
    api.changeFoodDescription(request.body.name, request.body.newDescription, function(result) {
      response.json(result)
    })
  }
})

module.exports = app

Let's get the api.changeFoodDescription() function created.

Open the /routes/foods/api.js file and add this function to the module.exports object:

changeFoodDescription: function(name, newDescription, callback) {
  // next code block goes here

}

When the changeFoodDescription() function is called, it will take name, newDescription, and a callback() function as parameters.

Before we can update the food, we need to verify that it exists in the database.

To do that, update the function's code to the following:

changeFoodDescription: function(name, newDescription, callback) {
  FoodModel.findOne({name: name}).exec(function(error, food) {
    if (error) {
      callback({error: true})
    } else if (!food) {
      callback({notFoundError: true})
    } else {
      // next code block goes here

    }
  })
}

In this code, we use the findOne() query method to get the food from the database with the given name property.

If that food doesn't exist, a notFoundError error message is returned and we don't make any updates in the database.

Once we've verified that the food exists, we can make updates to it.

To do that, update the function's code to this:

changeFoodDescription: function(name, newDescription, callback) {
  FoodModel.findOne({name: name}).exec(function(error, food) {
    if (error) {
      callback({error: true})
    } else if (!food) {
      callback({notFoundError: true})
    } else {
      food.description = newDescription

      food.save(function(error) {
        if (error) {
          callback({error: true})
        } else {
          callback({success: true})
        }
      })
    }
  })
}

In this code, we set the new description value for the food and save the changes to the database using the food.save() method.

When the updates have finished, a {success: true} value is returned by the function using the callback().

Let's put this API route to the test!

Here's what the Postman request would look like:

Postman PUT Request Screenshot

And the curl request:

curl -X PUT -H  "Content-Type: application/json" -d '{"name": "Tacos", "newDescription": "Chicken and cheese in a corn tortilla."}' http://localhost:5000/foods/change-food-description

If the food document was successfully updated in the database, you should get this response:

{success: true}

And when you view your database in the Atlas dashboard, you should see the changes have been made.

Advertisement
Find Remote JavaScript Coding Jobs
RemotelyWeCode.com is the leading job board for coders that work remotely. Check out our curated collection of JavaScript jobs.
Find Jobs

Delete Database Data

Last, let's go over how to remove a food document from the database.

Open the /routes/foods/index.js file and add this code block to it:

app.delete("/foods/delete-food", function(request, response) {
  if (!request.body.name) {
    response.json({error: true})
  } else {
    api.deleteFood(request.body.name, function(result) {
      response.json(result)
    })
  }
})

This code configures your REST API to handle DELETE requests at the "/foods/delete-food" URL endpoint.

We'll use the food's name property to find and delete it from the database. Therefore, we need to verify that the request body contains a name value before moving on.

After verifying the existence of the request.body.name value, we call a api.deleteFood() function that's imported from the /routes/foods/api.js file.

Let's create the api.deleteFood() function next.

Open the /routes/foods/api.js file and add this function to the module.exports object:

deleteFood: function(name, callback) {
  FoodModel.findOne({name: name}).exec(function(error, food) {
    if (error) {
      callback({error: true})
    } else if (!food) {
      callback({notFoundError: true})
    } else {
      food.remove(function(error) {
        if (error) {
          callback({error: true})
        } else {
          callback({success: true})
        }
      })
    }
  })
}

In this function, we use the findOne() query method to retrieve the food with the given name property from the database.

If the food document is found, we remove it from the database using the food.remove() method.

After the document is removed from the database, we return a {success: true} result.

Let's test the API route!

Here's what the Postman request would look like:

Postman DELETE Request Screenshot

And the curl request:

curl -X DELETE -H  "Content-Type: application/json" -d '{"name": "Tacos"}' http://localhost:5000/foods/delete-food

If the food was successfully removed from the database, the response should look like this:

{success: true}

When you view your database in the Atlas dashboard, the food document should be removed.

That was the last step in this guide.

Conclusion

At this point, you should have a REST API application bootstrapped using Node.js, Express.js, & MongoDB. And you should know how to add, get, update, and delete data from the database.

Thanks for reading and happy coding!

About the author

Hey, I'm Nick, a software developer from Wisconsin, USA.

My latest course Build A Hacker News Clone walks you through how to build a clone of Hacker News using React.js, Node.js, MongoDB, & friends.

I also built and run RemotelyWeCode.com, a job board for remote coders.

Picture of Nick Major.