DEV Community

Kevin Downs
Kevin Downs

Posted on

Building A Basic API Using Express, Node, and MongoDB

Over the past week or two I have been trying my hand at learning MERN stack and building a simple tutorial project to play around in the code. For those that aren't familiar, MERN is an acronym for MongoDB, Express, React, Node, which happen to be the four technologies that make up the stack. I started by creating my back end API endpoints, using Express and Node to set up my server and MongoDB for the database. I'm going to break this down into a few different points so that you can hop back and forth if you already have some familiarity with any of the concepts/steps.

1. MongoDB Setup
2. Packages
3. Creating the Server
4. Models
5. Routes

MongoDB Setup

There are a few different ways to get your MongoDB database created and connected to your application. One of the main ways is by downloading MongoDB directly and creating the server on your own machine. A lot of developers do this, but for the sake of this project we are going to use mongoDB Atlas since it is a quick and easy way to get things running. To get started, all you have to do is log in or create a free account.

After logging in, create a new project by clicking the "New Project" button. Type in a name for your project and click the next button, and then on the next screen click the "Create Project" button. Next you will need to build the cluster for your project by clicking the "Build a Cluster" button and selecting the free shared clusters option. The last thing we will need to do is select a cloud provider and server location from the options provided. Personally, I chose AWS since they had the closest server to my location, but this part is up to personal preference. The other sections shouldn't need to be changed, but you can do things like change the cluster name as well if you would like. When you're happy with everything just click the "Create Cluster" button and wait while your cluster is being built. This can take 1-3 minutes so be patient if it takes a little while.

We won't be using this next section until we start building our server, so you can either grab the information now and set it aside until we need it, or just come back to this section once you start on the server. Either way, we now need to get what we need to connect our application to our newly created database cluster.

Go to your cluster and click connect. Here you will need to add an IP Address (current ip works fine if you're just using the machine you're working on) and create a user which we will use to connect in the next step. Click "Choose a connection method", and for this project we will select "Connect your application". If not selected already, choose Node.js as your driver and copy your connection string. This is what we will use in our server to connect to our database. It should look something like this:

Alt Text

Packages

Next I wanted to talk briefly about the packages we will be using. The first is obviously express which is a light web framework for Node.js that we will be using to help build our back end server. We will also be using cors, dotenv, mongoose, and nodemon.

cors stands for cross-origin resource sharing and allows us to access resources outside of our server from our server. dotenv will make it easy for us to use a .env file to store sensitive variables such as our database username and password. mongoose will help make interacting with MongoDB in Node.js simpler for us. Lastly, nodemon will automatically restart our server for us whenever we make changes to our files.

Now that we have a quick overview, let's get everything set up. Navigate into your root backend folder and create your package.json file by using npm init -y.

Next lets install our dependencies by using npm install express cors mongoose dotenv. Lastly let's install nodemon globally by using sudo npm install -g nodemon

Now we're ready to start building our server!

Creating the Server

The first thing we will need to do is create our server.js file and require the files we will be using. I also include the code here to configure dotenv to allow us access to our .env file.

// Require packages
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');

// Configure dotenv
require('dotenv').config();

Next we will create our server by assigning express() to an app variable and create a port variable that we will pass into listen() later to tell our app which port to listen on.

// Middleware
app.use(cors());
app.use(express.json());

Here we just tell our app to use the middleware that we want.

// Connect to mongoDB server
const uri = process.env.ATLAS_URI;
mongoose.connect(uri, {useNewUrlParser: true, useCreateIndex:true, useUnifiedTopology: true});
const connection = mongoose.connection;
connection.once('open', () => {
    console.log("MongoDB database connection established successfully");
})

This code is connecting our server to our MomgoDB Atlas database. The uri variable will load our connection string that we got when setting up our cluster from our .env file. We pass that along with some object flags into mongoose's connect function. (the flags are to deal with depreciation issues) Lastly, we just have a console log that will run when the connection is open so that we know it worked.

// Require and use Route files
const usersRouter = require('./routes/users');

app.use('/users', usersRouter);

Here, we are going to require and use our router files. If you include this before creating the files, it will cause an error so you may want to comment this out until we need it. Just know that this is how we include routes in our server. You can follow the pattern of:
const <model-name>Router = require(<file-path>); and then
app.use(<model-root-path>, <router-variable>);

// Server listen
app.listen(port, () => {
    console.log(`Server is running on port: ${port}`);
})

The last thing we need to do is tell our server which port to listen on which we do by passing our port variable into app.listen(). Here we also include a console log that will tell us which port our server is running on.

Models

Creating our models is rather simple. MongoDB is a document or noSQL database and so each instance of our example User model will be stored as a document similar to json. To create this model, we will add a models folder inside which we will create a user.model.js file. This is where we will be writing the schema for our database model. This is relatively straightforward so I will just post my code below and then explain it.

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const userSchema = new Schema({
    username: {
        type: String,
        required: true,
        unique: true,
        trim: true,
        minlength: 3
    }
}, {
    timestamps: true
})

const User = mongoose.model('User', userSchema);

module.exports = User;

So first we require mongoose and then create a Schema variable. We then declare a new variable userSchema which is a new Schema and pass an object into it.

The keys in this object will be what properties we want for our model. In this case we only want our users to have a username to keep things simple. Each key's value will be another object that will specify our validations for the property. You can see that we want our username to be of type String, be required and unique, trim off excess white-space, and have a minimum length of 3. We also pass in a second object that specifies we want to model to also have a timestamp. MongoDB will automatically add indexes for us as well.

Lastly we pack everything up in a variable and export it.

Routes

Homestretch, we're almost there! The last thing we need to do is create the endpoint routes for our API. Create a new routes folder, and inside of it create a users.js file for us to work in.

const router = require('express').Router();
let User = require('../models/user.model');

The first thing we will do in our file is require express router as well as our user model file.

router.route('/').get((req, res) => {
    User.find()
        .then(users => res.json(users))
        .catch(err => res.status(400).json(`Error: ${err}`))
});

This is the code for our /users/ route when our server receives a GET request. This implementation simple returns a list of all users if successful or an error code and message is it is not. You can easily edit this to include whatever functionality you wish.

The basic pattern here is calling route on router and passing in the desired route. Then we chain on the method for whatever request we want to handle (in this case GET) and then pass in a function that takes a request and response as arguments. These correspond to the request received by the server, and the response that it can send back after.

We are using the find() method on our User model to find all users in our database. This returns a promise which we handle with .then() and .catch(). If successful, we set our response to be the list of users we got as json. If not, we set the response to relay the error code and message.

module.exports = router;

Lastly we export so that we can connect the routes to our server file. Remember that part I said wouldn't work until we had the file set up? That's this.

Using the route declaration pattern above, you can easily include more routes for different request types and routes. This is an example of what a POST request to add a user might look like.

router.route('/add').post((req, res) => {
    const username = req.body.username;
    const newUser = new User({username});

    newUser.save()
        .then(() => res.json('User added!'))
        .catch(err => res.status(400).json(`Error: ${err}`))
});

Conclusion

That's really all there is to it. I know this guide is a bit lengthier than what I normally post, and there is definitely a lot of information, but if you are already familiar with building back end APIs using other stacks or frameworks a lot of this should look somewhat familiar.

This is certainly a very simple overview, but I'm sure you can see how using the patterns explained above you could expand this into a more meaningful API that you could use to serve data to your front end React project. I hope you learned something new!

If you liked this post, feel free to follow me elsewhere on Twitter, Github, or LinkedIn. Happy Coding!

Latest comments (0)