Building a Simple Node.js Express API

At the end of my last post, Connecting MongoDB to Node.js, I walked through a brief example of how to connect a local MongoDB server to a Node.js Express app. We finished with an app that basically does nothing except create a model for a Todo and connect to a local Mongo database. So if you aren’t caught up there, please read that post before you continue.

Now that we have our Todo model configured and our express app is connected to the database we can start building out this simple API to handle CRUD operations. We are going to start with the first part of CRUD, which is create. You don’t have to start here but it’s common practice to write the create functions first for a few obvious reasons. So let’s dive into the handlers for our todos and get started. You can cd into the handlers directory and create a new file todos.js. Inside this file, we are going to write all the code to handle any request that comes in for a todo. So let’s take a look at how this is going to look for our create function.

So lets break down this code line by line. First, we require our model’s directory which will let us access the exported models in our handlers. You can see that I like to comment in the route and request type that will invoke this function but that is just my preference. Next, on line 4 we export and declare the async function createTodo which will create a new Todo in the database for us. We need to export it so we can attach it to a route later. The function takes two arguments, req will represent the incoming request from the client and res will be the response we send back to the client. Since this is an async function we are going to use a try...catch block to make sure things work as expected. So on line 6, we are invoking the .create method on our Todo model. Note that we are using await because this is an asynchronous request to our local database to create a todo. With mongoose our models are essentially constructors that provide an interface to interact with our MongoDB collections and the documents in those collections. Each document that is inserted is an object that is created from our Model and we have a lot of cool methods we can run on those objects. We will cover four basic methods in this post, .create, .findById, .find and .remove. I’d encourage you to check out all the methods available in the docs here.

For .create we need to pass it any data that is specified as required by the model it’s being invoked on. Our Todo model just requires a name for the todo, so that’s all we are passing to .create. We are grabbing the todo name from the body of the incoming request, which is where we get on line 7. Breaking this down further, req represents the request object coming from the client, inside that object there is a body object (thanks to body-parser which we installed earlier) and inside the body there will be the key name and the value will be the name of our new todo. So, a high-level overview here in case you aren’t following, we are handling a request from a client(which will be an ajax call we make on the front-end) to a specific url(route) and we will use JSON to grab relevant information that was sent in the body of the request. You can also grab information from other parts of the request like the url params, the headers, etc, and I will go over that in a little more detail later.

Okay, so we are awaiting what the .create method returns and assigning that to the const todo. Mongoose methods use promises which you may have read in the docs. Since we are working with Promises we can use the keyword await when invoking any of the mongoose methods. You can also use a .then .catch instead of using an async function with the try..catch block and the await keyword. However, when you start invoking many methods in one handler this syntax can get a little messy or hard to follow, but it’s really up to preference. In the mongoose docs, you will see they give examples using .then and .catch. So our todo const will represent whatever is returned from the .create method. If everything works correctly, todo will represent an object containing the data for the newly created todo. When we successfully add a document to our Mongo database the unique _id field is always automatically generated for that document. Next on line 9, we are going to leverage the unique _id and use another mongoose method .findById to run a final check and ensure we can find the new todo. Then on line 10 we are sending back a response to the client with the createdTodo that we just found. If there are errors we catch those and send that back to the client instead. Inside the error handler, we use logic to ensure that we always send back an error message “Unable to reach server.” with a status of 500 if there is no other information available.

Now our createTodo handler is ready to start creating todos. Make sure you save that todos.js handler file and then cd into the routes directory. Create a new file named todos.js inside the routes directory. That file will look like my gist below.

This is kind of boilerplate Express router stuff so there isn’t too much going on here to explain in detail. First, we are requiring express, the express router, and our createTodo handler. On line 5 we are telling express that any incoming post to the route /add should invoke the createTodo handler. We are going to prefix all our routes with /api/todo so it’s important to note here that /api/todo/add will be our complete route for this post. How you configure the routes is really up to preference, but generally, the prefix lives in the server’s index.js. This way, you will just need to type out the prefix once for each route and this reduces duplication/typos and is easier to maintain. On the last line, we export our router so we can use it in our server’s root index.js file. Now let’s save the todos.js file in our models directory and cd back into our server’s main index.js to configure the last piece of our route. We are going to update index.js to look like the gist below.

Not much has really changed in this file. On line 3 we bring in our todoRoutes that we created earlier. Then on line 9 we tell our app to use those routes anytime a request comes into the /api/todo endpoint. Our prefix is /api/todo and inside the todo route file we specify the last part of the endpoint /add. Now after saving these changes, we should finally be able to start sending requests to that endpoint and create todos. At this point, wrapping your head around this entire process may be difficult, however, I promise after you write a few routes it will become second nature. Basically, there are 3 interconnected pieces to our route. Our handler is the bread and butter of the route and actually “handles” the incoming request. The file inside the route directory organizes the routes for a specific route type and tells express how to handle each endpoint. Inside the servers index.js file is the last piece of our route. This is where the route’s prefix is defined and where we tell the app to use the routes for that prefix. We just have one route so far, but this 3 piece model really helps keep things organized and easy to maintain as we add more routes.

Alright, let’s actually start creating some todos. For API development and testing I really like to use Postman. There are a few different ways we could send request’s to our express app, but for simplicity, I recommend downloading and using Postman. Once you have Postman up you can start by putting the url into the bar at the top that says “Enter request URL”. Our route to create a todo is http://localhost:8081/api/todo/add. Then, look to the left of the url box and change the request method to be a “POST”. You can then click on the “Body” tab which is right under the url bar. In the body tab choose the radio selection “raw” and to the right of that where it says “Text”, change the value of that dropdown to be “JSON (application/json)”. Now inside the big textbox below the radio selection, we can type out the body of our request. To create a todo we just need to send the name in the body. So your body should look something like my gist below.

Once you have the body configured correctly you can hit the blue “Send” button at the top to the right of the url bar. Almost immediately you should see the response come in and the response body will appear in the textbox at the bottom of Postman. You should see the newly created todo in this textbox if everything worked correctly. If you get something else, there is an error somewhere in your code and the error message should give you an idea of where to look. A common mistake is forgetting to use the keywords await and async correctly. Of course, also ensure that your MongoDB and express app are both up and running without errors.

Now that we are adding todos you might want to take a look at the data. Two popular options are the Mongo CLI or Mongo Compass. We can also now write the route for a GET request so our API can send our todo’s to the frontend. So cd back into the handlers folder and open the, todos.js handler file. Add another handler function named getTodos. The function will look something like my gist below.

This function is a lot like our createTodo function so I won’t go into too much detail here. Just know that when you use .find and don’t pass in a query object it will return all the documents for that model in an array. If you wanted to query a collection you can pass an object to .find. For example, finding all the todos that are completed would look like db.Todo.find({completed: true}). So save this file and now head back to our /routes/todos.js file to start configuring the route for this GET request. At the top of the file on line 3, add the new function getTodos to the required imports. Below the POST route add a new route for the getTodos with the endpoint '/'. So sending a GET request to /api/todo will invoke our handler and retrun all the todos. Since this endpoint is under the '/api/todo prefix we don’t need to add anything else to the server’s index.js file. Your /routes/todos.js should now look like my gist below.

Now that we are able to get our todos, grab the _id from one and save that in a notepad. We will use the _id in the last two steps to update and delete a todo. First, let’s write the handler to toggle a todo’s completed property. This is going to look something like my gist below.

Add this function to the /handlers/todos.js file. For this handler we are going to use the routes params to send our todo’s _id in the request. To access the param id in the handler in this way, we just need to set it up correctly in the /routes/todos.js file, and I’ll show how to do that when we add the route for this handler. Let me just point out a few things, on line we are using our old friend findById to get the desired todo. Once we have that todo it is just an object and we can perform any necessary updates on it. So on line 5 we are setting todo.completed to be the opposite of whatever was there currently. Then we invoke .save() on the todo and note that we need to use await with .save(). Now we can add the route for this handler to our /routes/todos.js file. At this point, you should have a pretty good idea of how to do that. This time we just need to specify an id param in the route’s url. After adding that, your /routes/todos.js file should now look like my gist below.

In this route, you can see that we declare any params in the url by putting a : before the param we want. So the full route for this handler will be /api/todo/completed/XXXX and XXXX represents the todo id that you saved in a notepad earlier. Finally, we can add one last route to delete a todo. This route is going to be similar to our toggle completed route but it will be a DELETE request and will remove the todo instead of updating it. The handler for this is going to look something like my gist below.

This handler will use .findById again to get the desired todo, and then we invoke .remove() to delete that todo. We send back the deleted todo in our request so the frontend can handle the change. We just need to add the configurations for this route to our /routes/todos.js file to finish setting up this handler. I won’t go over that last one, you should be able to figure it out by now 🙂 Now that we have this little api up and running, in my next post I will go over how we can start building something on the frontend to interact with the api and display our todos in the browser.

Leave a Reply