BLOGAnnounced at MongoDB.local NYC 2024: A recap of all announcements and updates — Learn more >

Building a REST API with Express, Node, and MongoDB

Follow along with this tutorial to add MongoDB Atlas as the data store for your applications, by creating NodeJS and Express Web REST API.
Get started with Atlas
Sign in

JavaScript is the top web development language today, running in millions of applications and websites. Modern JavaScript applications use REST APIs built with Node.js and a user interface built with a JavaScript framework such as React or Angular.

The most popular framework for building restful APIs is Express. It provides support for http requests out of the box and has an intuitive syntax for the standard http methods used in the REST principles.

For all your data storage concerns, you can easily integrate MongoDB with the native driver available to you. Using this stack, you can leverage MongoDB's document model with the REST API standard payload, which uses the JSON format.

This article will provide a step-by-step tutorial on how to use Express with MongoDB Atlas, our database-as-a-service platform, to expose restful API endpoints for our client-side application.

Table of contents

The project architecture

You can explore the complete project in the following:

main branch - finished project

stub branch - starting point for following this tutorial

Our application will follow the standard REST architectural style. You will have client and server components. The application will send client requests to our server. The server will then fetch the data from the MongoDB Atlas database and return it to the client.

Finally, our front-end application will be written in React to use the REST API endpoints hosted on the Express.js server. The application is a blogging platform that uses the sample_training database, containing a posts collection.

Our back-end application will use the more modern Ecmascript module (ES Modules) syntax.

The application uses a standard client server design
The application used for this tutorial will be a blogging platform.
Project Tree

Here are the main files in the project:

The “server” directory hosts the Express.js server application and its dependencies. The main files here are:

  • .env: Configuration file holding Atlas connection string details.
  • db/conn.mjs: Exposes a global connection to the Atlas database.
  • index.mjs: The main entry point for the Express server.
  • loadEnvironment.mjs: Loads up the environment variables.
  • routes/posts.mjs: Exposes the REST API endpoints and performs their business logic.

The “app” directory is where the front-end React application code resides. The main files here are:

  • App.tsx: Front-end React code that manages the different routes of the front end.
  • /components: A folder with the reusable components you will use.
  • /pages: All application pages have their matching file in this folder.
Setting Up the Project

First, you will need to deploy an Atlas cluster. You can follow the Getting Started with Atlas guide to learn how to create a free Atlas account, create your first cluster, and get your connection string to the database. Be sure to also load the sample dataset.

Let's go to the “server” directory of the project and install the needed packages:

You can start the server right away. It will automatically reload every time you save one of the files in the project.

Now, we are ready to connect our Express server to the MongoDB Atlas Cluster.

Set up the environment variables

Edit the .env file to assign the ATLAS_URI variable the value of your connection string. Replace the credentials with your database username and password.

Our application uses the dotenv package to load this .env file to add these values to the environment variables usable by the code.

You will need the following code in the loadEnvironment.mjs file to load these environment variables.

The entry point script can then import this file, which will be executed immediately. In index.mjs, add the following line at the top.

The Atlas connection string will now be available to our other modules.

Create the database module

Next, open server/db/conn.mjs and replace the content with the following code. This code will create a global database object that the other server components can reuse.

This code uses the connection string provided in the .env file and creates a new client. Once the client is defined, it tries to create a new connection to the database. The sample_training database is then exported if the connection is successful. This gives us a uniform interface that can be reused in all modules. ##Handling the HTTP requests

The core of this tutorial is to expose REST API routes to perform Create, Read, Update, and Delete (CRUD) operations for our restful web service.

In this sample application, all the routes for our server are located in the file server/routes/posts.mjs. We then tell our server to use this module for all the incoming requests to routes that start with /posts. This is done in the index.mjs file in the following line.

The code is already there, so you don't need to add that. We will now focus on the logic for all of the API requests.

Application architecture, and the six routes you will create.
There will be six routes, each performing one of the CRUD operations.
Read route

The Read route will be returning 50 of the articles when there is a get request on the /posts route. In server/routes/posts.mjs; find the GET route and replace the code with the following.

Here, we start by defining our collection object, then do a .find operation. We limit the results and convert the response data to an array. We can then send data to the client using res.send. We can also specify the status code with the status method. Code 200 stands for "OK, " meaning that the operation was successful.

Read route (advanced)

You can also create a more complex route using aggregation pipelines to return a result. For example, we can add a route that will return the three most recent articles in the collection.

As you can see here, the code is similar. This endpoint will catch all the get requests to /post/latest. We then use an aggregation pipeline to sort the collection in descending order of date and limit the results to three. This leverages the database to do the heavy lifting with our data.

Read single result

You can also use parametrized routes to return filtered results or, in this case, a single object. Notice in the following code snippet that the route takes an :id parameter. You can then access the value of that parameter with req.params.id.

In this get request, the id is converted into an ObjectId using the method provided by the MongoDB native driver. It is a query filter to retrieve a single document with this unique id.

If no results are found, we can send a different response — a 404 error message, in this case.

Create route

The Create route will add a new post to our collection. To catch a post request, you will use the router.post method from Express to define this route. Based on the REST conventions, adding new items should be done with a POST method.

In this sample, we take the request body, append the current date and time, and save it in the collection direction with the collection.insertOne() method.

You can also use InsertMany to insert multiple documents at once.

Update route

The Update route adds a new comment to our blog post. Best practices in REST API design state that we should use a PATCH request for updates. Sometimes, a PUT request might also be used.

You might wonder why this is considered an update and not a create. This is because we are leveraging the MongoDB document model. Since we are not restricted to a flat structure like a relational database, we store all the comments as an array inside the post object.

In a relational database, we add new comments to a distinct table

To add a comment, we will update this array with a push operator with the request body as the new comment.

The route uses the collection.updateOne() method with the unique id specified as an ObjectId, and the operator as the second argument.

Delete route

And last but not least, we need to handle the delete request. The users will have the ability to delete an article from our blog.

Here, we are using a parametrized route to get the id of the object to delete. The post is then deleted with the collection.deleteOne() method.

Your server is now ready to be used. You can already test it with tools such as Postman or curl. For example, you could run the following command to get the latest articles.

Now, any third-party applications can connect to this server, but let's see how we can get our React application to connect.

Setting up the front end

Plenty of tutorials provide a lot more detail on how to query a public API and manage responses from a client request. Understanding the basics of React applications is out of the scope of this article, but you can see examples of how to access the API you just created in the /app/src folder.

In the /app/src/pages/Home.js file, we retrieve the latest three entries from the blog. This is done through a request to the /posts/latest route of the server. In this application, we are using the browser's native fetch object.

This loadPosts function uses fetch and passes the URL to the API. We could use the response in several data formats, but we are explicitly converting it to JSON, as it's the easiest way to manipulate data in JavaScript. The setPosts function updates the application's state and forces the user interface to re-render.

All requests to read from the API follow the same pattern. We use the following to fetch all the articles in the app/src/pages/Archive.js page.

The only difference here is the route. The same goes for fetching a single document in app/src/pages/Post.js.

We still use the fetch object to create a new entry, but you will need to specify the HTTP method, an additional http header, and the request body. The code to create, update, or delete entries can be found in /app/src/pages/Create.js.

Doing an update follows the same patterns but with a different method.

And the same is true for a delete request.

If you want to test the application, open a new terminal and run the following.

Testing the application

Once all components are up and running, we can open the http://localhost:3000 URL, and you should see the sample blog. From there, you can create new blog posts, read an existing entry, add a comment, or delete that entry.

Create a new post
Add a comment to an existing post
Browse all the existing articles

You now have a fully functional application that uses the REST principles. It has a back end that answers the necessary requests, and the client can request data from those endpoints.

Atlas App Services HTTPS endpoints as Express replacement

Atlas App Services, MongoDB's development cloud services, offer a robust and scalable replacement to the self-hosted Express Server.

Using Custom HTTPS Endpoints, you can convert this application into a serverless RESTful API. Using serverless technology will make your application much more scalable.

Summary

Using Express as a back-end framework is a popular MongoDB stack design. Express is lightweight and approachable for JSON and REST API operations. MongoDB Atlas is a scalable and flexible document database as a service and makes a perfect companion to Express in many stacks like MERN, MEAN, and MEVN.

Atlas App Services and custom HTTPS endpoints are a robust replacement for the Express tier, removing the need to manage an Express server and its dependencies on-prem.

FAQ

How do I use MongoDB with Express JS?
The MongoDB Driver is the native driver for Node JS applications. Therefore, the Express JS server can use it to access MongoDB.
How do I create a REST API using node Express and Mongo?
In this article, we covered the essential parts of setting up an Express server with connection to MongoDB Atlas as well as exposing REST API endpoints to the client applications.
Want to learn more about MongoDB and Express?

Visit the following links: