Build a REST API with Node, Express MongoDB & Postman!

staunchcfo

Emeka Okezie

Posted on October 26, 2020

Build a REST API with Node, Express MongoDB & Postman!

In this project, we will be creating a simple API that would allow people Update, Delete, Create, and Subscribe to a social media account. We will do this by coding the data the users will be interacting with within our social media database and how it goes about manipulating them depending on what a user wants to do.

Project Requirements and Dependencies

Important Prerequisite: Make sure you already have MongoDB installed and set up on your machine before starting this tutorial. Here is a link to a guide that MongoDB provides on their website: MongoDB Installation

After installing MongoDB, the next step would be the opening of your command line, then create a new directory for the project we will be working on using this command mkdir directory_name this would quickly create your new directory, then you would need to be in the newly created directory by using the following command cd directory_name this would give you access to the directory you have created.

In this project, we would be working with the express generator to quickly create our application skeleton. For earlier Node versions, install the generator as a global npm package and then launch it using the following command npm install -g express-generator. This would automatically install the express generator globally on your local machine, after installation, you would be required to type express in your command line, this will create all the required files to set up your express app skeleton

Alt Text

After creating our express app, you would be required to delete the views and public folders as well as the bin folder, since all we are creating is a simple REST-API, we won't be needing to make use of them, also, we will be required to take out the line of codes for the view engine set up and the middleware for joining the public directory to our app.js file.

Alt Text

Next, we will be installing Mongoose, which connects our application to the MongoDB Database using this command npm install mongoose then dotenv and nodemon for setting up our environment variables and restarting our local server respectively, after we saved our files with the .JS extension. this will be installed using the following command npm install --save-dev dotenv nodemon.
Note: --save-dev from above will save development-only dependencies without saving them to production

dotenv will allow us to pull in environment variables from a .env file. When we ran the express command earlier it created a package.json file. In this file under the scripts, we want to replace that ‘test’ script with our own script that starts our server with nodemon:

Alt Text

Setting up Our Local Server

Running the express command in the command line, after installing express-generator automatically created an app.js file. Once created, we will need to require all our packages at the top of our app.js file, for the file created using the express command, you will already have the express dependency declared at the top of the app.js file, all you will need to do is require the dotenv and mongoose packages.

Alt Text

When we want to test and make sure our server is working, we will run this function to listen on port 3000 and log a string if it's successful:

app.listen(3000, () => console.log('server started'))

Alt Text

Connecting to Our Database

Before running the database connection, first, you will be required to pull in the dotenv package you had initially installed, just the same way you did for express and mongoose like this const dotenv = require('dotenv').config(). The .config() function automatically configures the dotenv package to be used in your application entry point.
This next chunk of code in our app.js file starting with const CONFIG is how we will be connecting to the database. I’ll explain it line by line:

Alt Text

The OPTIONS object is actually optional for the purpose of this API, mongoose.connect(CONFIG.uri, CONFIG.OPTIONS) allows us connect to the database, you may be wondering the DATABASE_URL, this is the location where we want to store our database, which has been identified in the .env file created in the root folder of our API. If you haven't created a .env file, now will be a good time to go about this and set the DATABASE_URL there:

Alt Text

Moving on, we have to set db to mongoose.connection as a syntactically easier way to reference our database. Next, we’ve defined what happens when the database connection is successful, which is to log the string of connection to the database on our terminal.

Testing the Database Connection

At this point, we can now test out our connection to the database to make sure everything is working smoothly. In our terminal, let’s go ahead and start our database by typing mongod and then in another terminal tab let’s start our server with npm run dev. If all went well, we should get these messages after we start our server:

Alt Text

Cool! We’ve successfully started our server and our database appears to be operational.

Setting up our Server to Accept JSON

In our same app.js file we want to tell Express that it should accept JSON. Lets put this line of code in between our ‘database is open’ line of code and the ‘listen on port 3000’ line:

Alt Text

The .use is a middleware that allows you to run code when the server gets a request but before it gets passed to your routes. So in this instance, we’re telling Express to accept JSON as the data format.

Creating our Route File

Inside our directory, you will notice you already have a routes folder in it, we also have an index.js file, this is where we will define how the server should handle the data when it receives a GET, POST or PATCH/UPDATE request.
But before we start creating those, let's switch back to our app.js file and tell the server that we now have routes that it needs to handle and use. These next two lines of code are actually the last ones we need for our app.js file.
var indexRouter = require('./routes/index');
app.use('/', indexRouter);

Here’s the full server.js file with the new lines of code added:

Alt Text

If something is missing or incorrect, right now is a great checkpoint to make sure you’ve got everything updated with the above. Give yourself a pat on the back for getting this far! We’re getting onto the fun stuff next…

Setting up Our Routes

Now that our server file is all set up, we will now switch gears to our new index.js routes file. To start, let us require Express and define the express.router() function on a variable called router. While we’re at it let’s add our module export function as well at the very bottom:

Alt Text

Before we get into the code, let’s get an understanding of what we exactly want to do with our routes. Since we’re trying to see and artificially create new subscribers on Social App, we need this set of criteria:

Route for getting all subscribers
Route for getting one subscriber
Route for creating one subscriber
Route for updating one subscriber
Route for deleting one subscriber

Now let’s start each of the bullet points above with the basic framework depending on if its a GET, POST, or PATCH request:

To get this fully set up, we could be creating this API using the MVC method without a View, by this, I mean since it's just an API we won't be needing to render page here. We will be creating a Controller folder which will handle all of our routing functions, then we will be exporting these functions to our index.js file in the Routes folder. First, you should have these lines of code in your index.js folder for your routing.

Alt Text

Ignore all the errors, for now, we will sort out all of those very soon, you may go ahead in creating your controller folder and an indexController.js file.
The one thing that might look weird is the PATCH method. The PATCH method is being used here instead of the more traditional PUT method because we only want to use this to update pieces of the subscriber’s information and not all the information of the subscriber.
You also may have noticed that we include /:id in most of our parameters. This is for the routes that are requesting an action to be taken on a single subscriber thus we need that subscriber’s unique ID.

Testing Our IndexController File

In our indexController.js file we will be writing a chunk of code, this is with the aim of testing our API to see if it's well connected using our routing. I will be needing to explain the code below, later on, right now our routing functionality is now been declared in the indexController.js file then export to the index.js file in the router folder

Alt Text

Below is where the indexController.js file is being imported into the routing file.

Alt Text

What the above block of code means is this, in the indexController.js file, we used a class-based method in exporting all our functions, then we created an arrow function called testingRoute then assigned a response and require argument to it, afterward, we sent a simple JSON message 'Hello World' using the res.send() command in a try and catch block. What this does is to try the initial command in this function, in case there is an error, the catch block prints the error in the console, after this is done, we then export the functions inside the Class to the router file.
In the index.js file we then require the indexContoller.js file and assigned it to a new variable called IndexController, then using the GET method, we will be calling out the content of the indexController.js as shown in the code above.

Now for testing our API, I will be making use of Postman, you can download it from their official page Postman Installation. After you are done installing, open the Postman app, you will get the same page:

Alt Text

All you will be required to do is test the GET call to our Server
http://localhost:3000 using the Postman app, just click on the + icon on the app to access a new tab, then copy this URL and paste in the input field, first you need to select the action as a GET request, then hit the Send button, this would print the 'Hello World' text we sent through the indexController.js file as shown below:

Alt Text

This means our API is working correctly! Ok so we’ve done a lot so far, let’s take a hydration break, and then we’ll get started with models.

Making the Models

Let’s go ahead and set up our model and the schema inside of our model. A schema is how our app defines what the data looks like and also sets up the document in MongoDB. If that sounds confusing, it’ll make more sense once we see what’s going on.

Let’s first start by creating a new folder called models. Inside of this folder, let’s create a new file called subscriber.js. The idea is that the model will handle how each and every subscriber, on an individual level, will look inside of our database. The ‘index’ routes handle sometimes multiple subscriber's requests such as Get All Subscribers route. It’s an important thing to mention as verbiage is important when naming files in projects.
In this new file, we want to first require Mongoose since we will be using their schema models:
const mongoose = require('mongoose')
After we require mongoose, we’ll start by defining our schema
const mongoose = require('mongoose')
const subscriberSchema = new mongoose.Schema({})

Inside of this javascript object will be all of the keys for the different properties of our subscriber. These keys will include name, subscribedChannel and subscribeDate. We essentially want to tell our database what to expect from each one of these keys such as their type, if they’re required and if a default value should be applied.

Alt Text

The type and required properties should be pretty self-explanatory. These are defining the expected schema type (a String and Date in this case) as well if that key is required upon entering information for a new subscriber.

One thing to note about subscribeDate, we set the type to Date instead of String since we will be expecting a date from the user. If no date is provided then we default it to the current date by using Date.now.

Moving on, the last line of code we want to write in our schema is the module.exports. This will allow us to use and interact with the database using our schema. Mongoose has a special way of exporting models utilizing mongoose.model() that takes two properties:

module.exports = mongoose.model('Subscriber', subscriberSchema)
‘Subscriber’ is the name we want to give the model in our database and then the next one is the schema that corresponds to that model which is our subscriberSchema.

That’s it for our model! Let’s take a look at the finished schema to make sure we’ve got it all:

Alt Text

Creating and Getting Subscribers

Now that we have our model setup with a schema for our database, let’s require it in our indexController.js controller file below where we required router
const Subscriber = require('../models/subscribers')

We have our test response from earlier when we sent ‘Hello World’ to the server but we can go ahead and delete that since we want to actually tell that route to Get All Subscribers.

The first thing we’ll need to do after we get rid of our old res.send('Hello World') line in the indexController.js file, is wrap the function in a promise with a try/catch statement, just like we already did previously:

Inside of our try statement we want to get all of the subscribers from our model. So we want to set a new variable called subscribers to that model with a .find() method applied to our Subscriber model.

Alt Text

As the name implies, the find() Mongoose method works by returning all associated subscriber objects that meet its criteria. Since we’re returning all subscribers, we just leave the parentheses blank since we want all the subscribers:

After that line, we then want to send a response with the data of our subscribers variable we just created in the form of JSON. Finally, in our catch statement we want to just catch any errors that may occur and have it sent to the user as a 500 error in JSON format:

Alt Text

Now that we have our route to send us all the subscribers in our database, we need to code a way for us to actually add a subscriber into our database. So, lets move onto our Create One Subscriber controller so we can enter data about a subscriber:

Alt Text

You can see it's somewhat similar to our Get All Subscribers controller except for a few important differences. For one, we’re no longer doing a GET call on our database but a POST which will allow us to push data to our database.

On this line:

const subscriber = new Subscriber({...

we are creating a variable that will be assigned to a new Subscriber from our model that we created earlier. If you recall, we require a name, subscribedChannel and subscribeDate properties for a new subscriber.

These next two lines of code:

name: req.body.name,
subscribedChannel: req.body.subscribedChannel

We’re telling our controller to save the request made from a user’s input of a new Subscriber name property and subscribedChannel property. Our subscribeDate doesn’t need to be defined because it will default automatically to the date/time that this database entry is made.

The try and catch statements should look familiar. We’re instead using a .save() Mongoose method instead of find() because this is how we will tell the database that we want it to hold the information a user passes to us through this controller function.

Lastly:

...
res.status(201).json(newSubscriber)
} catch (err) {
res.status(400).json({ message: err.message })
}

We’re sending the user response with a success status of 201 and to pass our new subscriber back to us as JSON. The catch is just like our Get All Subscribers controller except we pass a 400 error since this would be a user error for passing us bad data.

To test all of this, in our index.jsroute file, after requiring the indexController.js file, for the Get all Subscribers route, this would be written out like this router.get('/', IndexController.getAllSubcribers) since it is a get request to all the Subscribers, for the create a single Subscriber, since it is a POST action router.post('/create', IndexController.createSingleSubcriber), mind you at this point you have your IndexController called up in the index.js file like we do in the code below:

Alt Text

...
IndexController.getAllSubcribers and
IndexController.createSingleSubcriber

This is just a way of targeting the functions inside the indexControler.js file, remember we recently declared these two functions to get all subscribers and create a single subscriber, we do this by appending the functions of the indexController.js file to the IndexController variable which was declared in the route file const IndexController = require('../controller/indexController').

Now to actually test all we have done, we will be create a new subscriber using the create route we just created, remember it is a POST action. we will do this by typing the following url in our Postman application. http://localhost:3000/create, the /create/ path was declared in ourindex.js` route file. Before hitting the create route, we need to do a quick set up in Postman to be able to pass in raw JSON data

Alt Text

First, we need to select the Body(in green), then the raw option, afterward we then select JSON from the drop-down option, this would make POSTMAN know we are about passing a JSON data,
Once you do this as shown above you can then manually type this in the same format in the input space provided in POSTMAN
{
"name": "Robert",
"subscribedChannel": "Bennetts Channel"
}

If after you are done with this and hit the submit button, you should get this output:

Alt Text

We just created a new user, the user now has his own unique ID, If everything went well, our response when we click ‘Send Request’ should look like the above. We can see that we received our 201 success status at the very top along with our Subscriber object at the bottom with all the information we just plugged in.

Again, subscribeDate is set automatically to the date of creation so we don’t need to manually set it.

At this point, we can easily get the number of subscribers in our database by just hitting the http:localhost:3000/ URL, but this time with a GET action, after hitting the send button, POSTMAN would print out all SUbscribers in the Database.

Get Single User

Alt Text

The first line on this code looks pretty familiar already, in the try and catch block, we used a MongoDB method of findById to target a particular ID from our Database, where (req.params.id) The req.params property is an object containing properties mapped to the named route “parameters”. For example, if you have the route /student/:id, then the “id” property is available as req.params.id. This object defaults to {}. Now, we are mapping through the Subscriber Database to search for a particular ID,findUser == null implies that if the subscriber is not in the Database, the next command in the function is to return a status code of Error 404 with the message of 'Cannot find the subscriber' else res.status(201).json(findUser) simply implies that the subscriber found should be printed in JSON with the status code of 201, which means that everything is working well.
At this point your index.js route file should be looking like this:

Alt Text

To get a single Subscriber, we need to first GET all the Subscribers, using the URL http:localhost:3000/ in your POSTMAN, then copy a Subscriber's unique ID, after doing this you can now confirm if your function is working well by hitting this link http:localhost:3000/single/theUserId this should get you the single User with the status code of 201.

Delete Subscriber

Alt Text

Just the same way we did for the GET single Subscriber by using the MongoDB command of findById, in this case, we had to do a little different thing, after finding the users by their ID using const findUser = await Subscriber.findByIdAndDelete(req.params.id) the next action was confirmed is the User/Subscriber really exist in the Database, if so, remove this user using the following command Subscriber.remove() where Subscriber is the model the user is located and remove() is a MongoDB function to remove a data from the database, after you are done, your code should look just like what we have above, if the subscriber is not in the Database, the next command in the function is to return a status code of Error 404 with the error message. After this has been done successfully, your delete route in the index.js route file should and update and would look like this:

Alt Text

Also if you try deleting a Subscriber, after selecting their ID on POSTMAN, you should get this result too:

Alt Text

--NB: This has got to be a DELETE action also like you did for GET to get all Subscribers or single Subscriber--

Patch/Update a Subscriber

Our Update Subscriber route is the very last thing we need to write for this application to be fully functional! Ok so before we get into the code lets get a general idea of how updating will work in this case:

User updates just the name
User updates just the channel
User updates both name and channel
Or they mess up and it throws an error
Our requirements need for us to essentially check and see if any changes were made and if so, update them appropriately. Now onto the code:

Alt Text

Using the same method as wit the previous, the first line of code remains familiar,

...
const updateUser = await Subscriber.findByIdAndUpdate(req.params.id , {
name : req.body.name,
channel : req.body.subscribedChannel
}

In this case, we are using a method of findByIdAndUpdate, to map through the Database, and then if the particular that has been inputted is actually in the Database if so, we are targeting the name and subscribedChannel from the Subscriber model, and either update both fields or any one of them, if this operation is okay, print the JSON message.
If your code is correct and is the same as the one above, the next operation would be updating your index.js route file, which would finally look like this:

Alt Text

Afterward. we can now test this route with the POSTMAN. first, we will get a single user using his Id and this link,http:localhost:3000/single/5f967dedbab34829a4eb83ee. this will be a GET request, after we get this User, and update his/her record, we can then PATCH the user using this URL http:localhost:3000/update/5f967dedbab34829a4eb83ee and his the Send button, we will get the result below:

Alt Text

You can confirm that the User details really got updated by either get just that user again or getting all the users as shown below:

Alt Text

Conclusion

Guys, we made it. I really hope this tutorial was helpful to some of you. We went over a lot of stuff so if you’re feeling overwhelmed then that's totally understandable. But realize we just made a pretty awesome backend piece of code that translates into so many different real-world applications. So big props to you for making it through it all!

The complete code can be found in the Master branch of my GitHub repo

If you ever got stuck or found something worth mentioning, go ahead and drop a message on Twitter or leave me a comment below.

💖 💪 🙅 🚩
staunchcfo
Emeka Okezie

Posted on October 26, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related