All About APIs (ft. Express & MongoDB) [nwPlus Workshop]

Create an Express.js backend server with a MongoDB database with simple CRUD operations.

Ben Cheung
World of Cultivation

--

nwPlus Backend Workshop

Upgrade your front-end project by learning how to build your own back-end using Express. In this intermediate-level workshop, you will learn how to increase the complexity of your project with an Express.js backend, MongoDB database, and your own API.

This article is a written version of the nwPlus Backend workshop found here:

View the workshop on GitHub here: https://github.com/MrBenC88/All-About-APIs-Workshop

Workshop Companion Notes: https://www.notion.so/Workshop-Companion-Notes-Guide-9f3f132f0a2b4e7aad28c0dfc7b07df9

Let’s dive into it!

Starter file: https://github.com/MrBenC88/All-About-APIs-Workshop
Solution file: https://github.com/MrBenC88/All-About-APIs-Workshop/tree/starter-workshop-solution

Main Steps:

  1. Getting the repo
  2. Setting up the Backend
  3. Connecting to MongoDB
  4. Creating Server Endpoints
  5. API Testing with Insomnia

Getting and Cloning the Repo

  • Open your VSCode/or any other IDE to a folder of your choice that you want to store your project.
  • Open a new terminal and make sure you are in your desired folder directory.
  • Clone the following repo into your current directory.
git clone https://github.com/MrBenC88/All-About-APIs-Workshop.git
  • Enter the backend directory and install the dependencies. Make sure that you are doing npm install in the correct directory! The directory that you need to install it in contains server.js
cd backend 
npm install
  • To run the server at any time, we can use the following command in our backend directory.
nodemon server

Setting up the Backend

Now, we will begin coding up the server.js file. This is our actual server file. This server is where we connect the MongoDB database and define the routes that we would use.

We will do the following steps as shown in the comments of the starter file.

  • Dependencies
  • Set up express function (app and port)
  • Set up middleware and use express.json method
  • Pass in our defined port variable so our server can accept a parameter from the environment and what port to listen to. (app.listen)
  • Confirm server is running.
  • Run server to confirm.

Let’s get started!

We will need to first add our required dependencies

  • express: node.js web app framework
  • cors: middleware for Express
  • mongoose: used for MongoDB and helps connect to mongodb database
  • dotenv: allows you to separate secrets from your source code — important for .ENV file.
//dependencies 
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
require("dotenv").config();

Next, let’s setup the express function.
Call the express function, express(), and puts the new Express application inside the app variable We also define the port we will be using for our webserver to listen on. In this case either a currently in-use port or port 5000.

const app = express(); 
const port = process.env.PORT || 5000;

We now need to set up our middleware and pass in our port variable so our server can accept a parameter from the environment and what port to listen to. We also want to confirm that our server is running. At the very bottom of our file, we will pass in our defined port variable so our server can listen to it and show a success message in our terminal.

Note:

  • cors is our middleware for Express.
  • express.json is a method inbuilt in express to recognize the incoming Request Object as a JSON Object.
  • Pass in our defined port variable so our server can accept a parameter from the environment and what port to listen to
  • Log to the console to confirm it is running.
app.use(cors()); 
app.use(express.json());

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

Let’s run the server to confirm!

cd backend 
nodemon server

The following output should be shown:

Connecting to MongoDB

After we have confirmed that we have a server running and have completed the barebones of the server. We can go ahead and connect our database using our credentials from our cluster.

Steps:

  • (TODO #1): Create .env file for MongoDB credentials
  • (TODO #2): Variable to store the info in the .env var. -> const uri = process.env.ATLAS_URI
  • (TODO #3): Connect to MongoDB Database
  • Set a variable so we can check that we are connected to the database -> const connection = mongoose.connection;
  • (TODO #4): Confirm the connection and output a success message if connected successfully via a function.
  • Define our user route -> const userRouter = require(“./routes/users”);
  • (TODO #5) Mount the middleware for the routes served by the userRouter -> app.use(“/users”, userRouter);

Let’s get started!

If you didn’t complete the workshop, feel free to use our dummy one below.

ATLAS_URI=mongodb+srv://nwPlusUser:nwPlus@backendworkshop.woyus.gcp.mongodb.net/BackendWorkshop?retryWrites=true&w=majority

Note: If the above doesn’t work, the cluster may have been deleted. It is super easily to run your own cluster. Follow the instructions found here: https://www.notion.so/Pre-workshop-All-About-APIs-6aff0df0ed224cd298a401e03ec39d27

As you will be using GitHub, if you don’t already have a gitignore, create that too!

  • Right-click on the Backend folder and click New File
  • Name your file .gitignore
  • In your gitignore file: copy-paste the following. This will tell git to ignore these files and not commit them to your local repository.
node_modules/ 
.DS_Store
.env

Now, we will create a variable to access the information in the .env file.

// TODO #2 - Create a a variable to access that information in the .env variable const uri = process.env.ATLAS_URI;

We must also connect our MongoDB database using mongoose. We set a variable so we can check that we are connected to the database → const connection = mongoose.connection;

// TODO #3 Connect to your MongoDB Database mongoose.connect(uri, {   useUnifiedTopology: true,   useNewUrlParser: true,   useCreateIndex: true, });  const connection = mongoose.connection;

We now add some code to confirm and log a success message to our console upon successful connection.

// TODO #4 Confirm the connection and output a success message if connected successfully. connection.once("open", () => {   console.log("MongoDB database connection established successfully"); });

Now, if you haven’t already. Make sure to test your server and make sure it outputs the success message.

cd backend
nodemon server

Define our user route -> const userRouter = require(“./routes/users”);
Below our dependencies, let’s add a new variable.

const userRouter = require("./routes/users");

We need to now setup a variable so that we can set userRoutes. We must mount the middleware for the routes served by the userRouter.We define the /users route so when we access routes related to the userRouter, we will need to access via: localhost:5000/users/

// TODO #5 Mount the middleware for the routes served by the userRouter 
// For all routes using the user schema, need append /users to the url.
// ie. http://localhost:5000/users/
app.use("/users", userRouter);

Defining our User Model

The next section is still part of setting up the connection to our MongoDB.

We must Define our User model.

  • We will create a user schema that will represent a single user. The single user object will have two attributes: a username and age
  • Navigate to \nwPlus Backend Workshop\backend\models\user.model.js
  • Add an additional attribute representing the user age. (TODO #6)
  • Set the appropriate data type for the age. (TODO #7)
  • Define user model and export.
  • Open the file user.model.js via \nwPlus Backend Workshop\backend\models\
  • Add the additional age attribute according to the prompt given in the file and set the appropriate data type.

Solution:

/**  
* This file is our model. We define what exactly we will put into our MongoDB database.
* In this exercise, we will create a user schema that will represent a single user. The single user object will have two attributes: a username and age
*/
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// TODO #6 Add an additional attribute representing the user age.
// TODO #7 Set the appropriate data type for age. (ie. Number)
// trim is whitespace at end - if include whitespace it gets trimmed off const userSchema = new Schema( {
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3, },
age: Number, // solution },
{ timestamps: true, }
);
const User = mongoose.model("User", userSchema);
module.exports = User;

Great! We now have setup our Express server via server.js and have successfully connected it to our MongoDB cluster with the correct credentials. We have also defined a userRoute and user schema that represents a single user. Next we will create the server endpoints. Essentially, this is the API part where you can do the following operations in your database:

  • Create
  • Read
  • Update
  • Delete

Creating Server Endpoints

We now navigate to \nwPlus Backend Workshop\backend\routes\users.js

  • In your IDE, open the file users.js which is found in your routes folder.
  • Each route we will define will be using an HTTP request so that we can perform our desired actions to our database.

We will focus on CRUD (Create, Read, Update. Delete) operations.

  • Add a user to the database
  • Delete user from the database
  • Update user in the database
  • Read user from the database

Steps:

  • Define router variable from express. Define the User variable making use of the schema we created in the previous step.
  • Create routes.
  • Export the router.
  • We have provided two examples that we will go through.

Review example GET ALL request.

/** GET ALL (GET REQUEST)  
*
* Access via: http://localhost:5000/users/
*
* Example route utilizing a get request to get all the users in the database.
* This route handles incoming HTTP get requests from the /users path
* User.find() is a mongoose method that gets all the users from mongoose atlas database.
* For the .then line, after it finds all the users it returns the users in json format that we got from database
* if there's error - return a error 400 with the message */
router.route("/").get((req, res) => {
User.find() //
.then((users) => res.json(users)) //
.catch((err) => res.status(400).json("Error: " + err));
});
/**
* GET ALL Alternative (GET REQUEST)
* Access via: http://localhost:5000/users/all
* Note that in this route, we simply avoid the router.route and directly call the .get function.
* As we have already used the "/" path (http://localhost:5000/users/) we will need to use a different path.
* Instead of returning a json of all the users, it will send a string response in a format other than JSON
*
* This is particularly useful.
* Try it out! Open your browser and paste "http://localhost:5000/users/all)". Make sure you are running the server though. */
router.get("/all", (req, res) => {
//endpoint for accessing all users in database
User.find()
.then((users) => res.send(users)) //Note here.
.catch((err) => console.log(err));
});

GET ONE (Exercise)
// TODO #8 Fill in the missing pieces of code in order to complete the following Route.

Solution:

/**  * GET ONE (GET REQUEST)  
*
* Access via: http://localhost:5000/users/:id
* Example: http://localhost:5000/users/5f4c647904dcad4a242735e8
*
* A route for getting a single user's information based on the user's MongoDB id.
* The :id is like a variable. This is object id that is created automatically by mongoDB. */
// TODO #8 Fill in the missing pieces of code in order to complete the following Route.router.get("/:id", (req, res) => {
//endpoint for accessing single user by id in database
User.findById(req.params.id) // find it by id
.then((user) => res.send(user)) //then return as json ; else return error
.catch((err) => res.status(400).json("Error: " + err));
});

POST ONE (Exercise)
// TODO #9 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function .save() saves the new user to the database.

Suggested Solution:

/**  
* POST ONE (POST REQUEST)
*
* Access via: http://localhost:5000/users/add
*
* This route is for adding a user to the database. It requires the user schema in JSON format to be filled in and the request set to POST.
*
* Example JSON { "username": "nwPlus Test User 01", "age":99 } */
// TODO #9 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function .save() saves the new user to the database.
router.post("/add", (req, res) => {
const username = req.body.username; //we assign the username to variable, and create new instance of username
const age = req.body.age || 0;
const newUser = new User({
username,
age,
});
newUser
.save() // save the new user to the databse
.then(() => res.json("User added!")) // return prompt that user is added; else return error message
.catch((err) => res.status(400).json("Error: " + err));
});

DELETE ONE (Exercise)

// TODO #10 Fill in the missing pieces of code in order to complete the following Route.

// Note: The function User.findByIdAndDelete(req.params.id) finds a specific id from the MongoDB database.

/**  
* DELETE ONE (DELETE REQUEST)
*
* Access via: http://localhost:5000/users/:id
*
* Delete a user based on their MongoDB id.
*/
// TODO #10 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function User.findByIdAndDelete(req.params.id) finds a specific id from the MongoDB database.
router.delete("/:id", (req, res) => {
User.findByIdAndDelete(req.params.id)
.then(() => res.json(`User with id ${req.params.id} deleted!`))
.catch((err) => res.status(404).json("Error: " + err));
});

UPDATE ONE (Put Request Exercise)

// TODO #11 Fill in the missing pieces of code in order to complete the following Route.

// Note: The function User.findByIdAndUpdate(req.params.id) finds a specific id from the MongoDB database and updates it

/**  
* UPDATE ONE (PUT REQUEST)
*
* Access via: http://localhost:5000/users/:id
* *
* There are multiple ways to update an existing user in the MongoDB database. One method is using a PUT request.
* The HTTP PUT request method creates a new resource or replaces a representation of the target resource with the request payload.
* The alternative shown in the next route is using a POST request to update the corresponding fields.
*/
// TODO #11 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function User.findByIdAndUpdate(req.params.id) finds a specific id from the MongoDB database and updates it
router.put("/:id", (req, res) => {
const body = req.body;
const user = {
username: body.username,
age: body.age, };
User.findByIdAndUpdate(req.params.id, user, { new: true })
.then((updatedUser) => res.json(updatedUser))
.catch((err) => res.status(400).json("Error: " + err));
});

Note that at the very bottom of the file we need to export our router.

//For all these router files, need to export router module.exports = router;

Great! We have finished the routes. Now, let’s test them to make sure they work!

API Testing with Insomnia (You can also use Postman/another tool)

Let’s make sure to navigate to the correct directory!

cd backend
nodemon server

Once we see our success message, then we can start testing. For any conflicting routes, comment them out. We can use test using a REST client like Insomnia or Postman.

For the purposes of this workshop, we will be using Insomnia.

  • After installing, create a new POST REQUEST using JSON
  1. New Request (Ctrl N)
  2. Enter name (anything)
  3. Select POST in dropdown and JSON for body.
  4. Create
  • Enter the url http://localhost:5000/users/add
  • Enter the following. Change the username to make it more unique. Add some characters or numbers at the end of it.
{  "username": "Test User 1003",  "age":4 }
  • Click Send and you will see a response.

If a successful response is obtained, You have added the user via a POST Request through the POST One Route.

If an error is returned, you should be able to view what type of error it is.

If we are successful, we would have created a new user in our database.

Now, let’s check that our user was added!
Check if your user was added by using the GET ALL request.
Steps:

  1. Set the request type from POST to GET.
  2. Change the url to http://localhost:5000/users/
  3. Click Send.
  4. If successful, you will have received a JSON response of all the users in the database.

Next test your routes for:

  • DELETE ONE (DELETE REQUEST)
  • UPDATE ONE (PUT REQUEST)
  • GET ONE (GET REQUEST)

Note: For /:id

Make sure the server is running. Otherwise, we navigate to \nwPlus Backend Workshop\backend\ in our terminal. Run the server.

Thanks for going through this!

Feel free to look at the full solution here: https://github.com/MrBenC88/All-About-APIs-Workshop/tree/starter-workshop-solution

--

--