DEV Community

Michael Wolf Hoffman
Michael Wolf Hoffman

Posted on • Originally published at codewithwolf.com

How to Build a REST API with Node and Express

Why Node and Express?

Node.js (AKA node or nodeJS) is a framework that allows you to write JavaScript on the server so that you can build your backend (server-side) code.

Before node.js, one team typically wrote the front end code using javascript, and another team would write the backend code in PHP, Java, C#, etc.

Node.js allows frontend developers to work on the backend of the stack and an entire team to communicate in one programming languange.

JavaScript is a powerful language and node.js allows that language to expand to the full-stack.

Express is a popular framework written on top of node.js to make writing server-side javascript easier.

Pre-requisites

It would be good to have at least some knowledge in the follow areas before beginning this tutorial:

1. A basic understanding of JavaScript (variables, functions, array methods)
2. Know what a REST API is and what it is used for.
3. Be familiar with HTTP request methods (GET, POST, PUT, DELETE)
Enter fullscreen mode Exit fullscreen mode

You will also need these system requirements:

  1. Node.js installed. You can install it here
  2. NPM installed. (Should be installed with node above, or here)
  3. An editor that you like working in. I use VS Code.
  4. A terminal you enjoy using such as cmd, powershell, or cmder
  5. Postman installed. Install it here

What we will build

We will build a very basic REST API for a todo-list app. This tutorial will have server side routing and the functionality to Create, Read, Update, and Delete items using nodeJS and express.

Getting Started


Before we get started, no tutorial will not be able to explain everything about building a node API. This is only the basics for beginners.

If you come across something you are unsure of, it is important to search around on google, stack overflow, forums, etc. A good software developer gets stuck, it is not a sign of weakness or ignorance. The difference between a good developer and a bad developer, is that when the good developer gets stuck, he or she can get themself unstuck by being resourceful and searching for the solution.

Take your time with this tutorial and try to understand each piece before moving onto the next.

This will help you a lot to understand node.js, APIs, and code in general

Start the project

  1. On a command line, navigate to the directory that you'd like to store your project in and create a new directory.
mkdir todo-api-node-js
Enter fullscreen mode Exit fullscreen mode
  1. Navigate into your new directory
cd mkdir todo-api-node-js
Enter fullscreen mode Exit fullscreen mode
  1. Use this command to create a new node.js project.
npm init
Enter fullscreen mode Exit fullscreen mode

This initializes a new node.js project. It will ask you many options in your console, but you can tap ENTER for all of these for now.

The Project Scaffold

So far, the project is very bare bones. You will only see a package.json file. If you taped ENTER and didn't change any options in the initialization process, you will see this in your package.json:

// package.json

{
  "name": "todo-api-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

This is an important file that will determine how your project runs.

It will have a name, description, scripts, and a few other areas. We will explain these more as we go along.

Installing NPM Packages

Any node API or project is made up of multiple npm packages.

NPM is 'Node Package Manager'. These are libraries that can be open source or third party that get installed into your project so that you can use their functionality.

They are usually very simple to install, we will install a few here and explain what they do as we add them into our main file later.

First, lets install all of the packages that we will need for this project.

npm install --save express body-parser nodemon
Enter fullscreen mode Exit fullscreen mode

The installation may take a few moments depending on your network connection's quality.

After the installation is complete and successful, your package.json file will have a new property called dependencies with the packages we just installed and a version number for each one.

// package.json

{
  "name": "todo-api-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "nodemon": "^2.0.6"
  }
}

Enter fullscreen mode Exit fullscreen mode

node_modules Folder

You will also see that your file structure changed to include a new folder called node_modules.

This is where these npm modules, dependencies, npm packages, or whatever you want to call them will be kept. (These names are all interchangable). There are likely hundreds of folders with several files each, just from those 3 dependencies that we installed.

We will soon get to how to use these dependencies.

.gitignore

Before I forget, let's add a .gitignore file. The reason we want this is if we add our project to source control using git, then we want to make sure we don't add this massive node_modules folder into source control. The file is huge and would slow source control down.

Add a file at the root level called .gitignore

Your file structure should now look like this:

// File Structure

- node_modules
- package.lock.json
- package.json
- .gitignore
Enter fullscreen mode Exit fullscreen mode

Lets open up the .gitignore file and simply add the text node_modules in there.

// .gitignore

node_modules
Enter fullscreen mode Exit fullscreen mode

Now, when we start using source control, all of these folders/files inside the node_modules directory will not be submitted into source control.

The Start Script

We now need to create a start script in our package.json file so that our app knows how to run. Let's open up our package.json file and add the start script.

Inside the scripts property, we can remove the test script that is added by default, and we should now add:

"start": nodemon index.js

our package.json now looks like this:

// package.json

{
  "name": "todo-api-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "nodemon": "^2.0.6"
  }
}

Enter fullscreen mode Exit fullscreen mode

What we did was tell node.js to run the index.js file to start our project.
One way to do this is to have the script read:

"start": "node index.js"
Enter fullscreen mode Exit fullscreen mode

If we did that, then every time we make a change in our project, we would have to re-start our server to pick up the new change.

Depending on the project, this can take a lot of time and if you ever don't remember to re-start your server after each change, you could be debugging forever before realizing why your changes aren't being seen in the project because you forgot to re-start the server.

With the npm package nodemon, node.js will see your change and re-start the server for you so you don't have to. You can read more about it here.

Starting our project

In your command line, you can now run

npm run start
Enter fullscreen mode Exit fullscreen mode

and our project will run!

All you should see now is an error saying module not found.

That makes sense because we are telling node to serve the index.js file... but we haven't created one yet. Let's do that now...

# The Main Project File: index.js

We have a lot of our basic scaffold set up to create a very simple REST API with node and express.

It's time to create an index.js file in our root and spend a lot of time there in the next few steps.

This is where we will begin to introduce our other two node modues: express and body-parser.

For now, let's try adding some very simple code in our root directory's index.js file.

// index.js

console.log("Hello World!")
Enter fullscreen mode Exit fullscreen mode

If we run npm run start now, we should see "Hello World!" printed to the console!

There will also be some messages about nodemon listening for changes in your console.

Because we are using nodemon, we can change the message in index.js, and when we save the file, nodemon will re-start our server for us and show the new message.

// index.js

console.log("Hello World Again!")
Enter fullscreen mode Exit fullscreen mode

If we save our index.js, we should now see the message "Hello World Again!" in our console.

(Without nodemon, we would have to stop the server with CTRL + C and then restart it with npm run start for these changes to appear. That is why I like to skip a step and just use the nodemon to begin with).

Creating a Server with express

express is a node module that allows us to write javascript to easily create server-side code.

Let's stay in our index.js and start a server with express.

We will remove our console.log and start at the beginning of the file by simply importing express into the file.

// index.js

const express = require('express')
const app = express()
const port = 5001

Enter fullscreen mode Exit fullscreen mode

The variable app is now our express server.

We also created a variable port which is where our app will run on localhost.

Next, we add a listener event and log a message when our server is running.

Our message will tell us what port our server is running on.

// index.js

const express = require('express')
const app = express()



app.listen(port, () => {
    console.log(`Node Todo API is running on port: ${port}`)
})

Enter fullscreen mode Exit fullscreen mode

If our node server is still running, we should see the message:

"Node Todo API is running on port: 5001" in our console.

If your server is not running, run it again with: npm run start.

Next NPM Package: Body-Parser

We have used the express and nodemon npm packages so far.

We have one more npm package we haven't used yet.

An API needs to be able to take data from the requests made to it. This can come in the form of route parameters (just like in the user interface, something like id in the route website.com/user/123), but also an API needs the ability to take data from a request's body.

body-parser will allow a node API to parse the request's body into a JSON object so our node API can use that data.

It is very simple to set up in our index.js. While there is a lot more to learn about requests and data, everything you need to know to get a basic understanding of a request sending data in a node.js API will be explained here very soon.

We need to import body-parser into our index.js

// index.js


const express = require('express')
const app = express()
const port = 5001
const bodyParser = require('body-parser');

...

Enter fullscreen mode Exit fullscreen mode

Then we need to set it up to use json.

Our index.js should look like this:

// index.js

const express = require('express')
const app = express()
const port = 5001
const bodyParser = require('body-parser');



app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.listen(port, () => {
    console.log(`Node Todo API is running on port: ${port}`)
})


Enter fullscreen mode Exit fullscreen mode

Where are we?

What do we have now?

Right now we have used our three node package: nodemon, express, and body-parser, to get to a point where we can start to add real API functionality.

A basic API should at the very least be able to perform CRUD operations (Create, Read, Update, and Delete).

We have an API that is running successfully and will be able to take data from incoming requests and process it how we need to in order to do our CRUD processes.

What's next?

Let's create our routes!

Routing

We are at a point where our API can start Creating, Reading, Update, and Deleting todos from a list.

Routing is a very important concept with node.js APIs.

Node.js works by listening to events on certain routes and then firing off actions when it "hears" an event on that route that it was listneing for.

Our routes are the system where we tell the node API what events to listen to, and when that event occurs we can run a handler which is a function that allows our API to process the data in the way we want it to.

A route can also be called an endpoint.

This will make more sense with our first route/endpoint...

Our First Route

Let's add our first route. It will be very simple.

in the bottom of our index.js file we will add this code.

// index.js

...

app.get('/', function(req,res){
    return res.send("Hello World!")
});


Enter fullscreen mode Exit fullscreen mode

In our first route above, our app (server) is listening for an HTTP GET request to the route '/'.

That means if we make a GET request to localhost:5001/, then the function (handler) in the second parameter above should run.

Pretty simple, eh? If that makes sense, then you understand how node.js works.

Acording to the code snippet above, if we make a GET request to the route '/', we should get a resonse that says:

"Hello World!"
Enter fullscreen mode Exit fullscreen mode

We can make GET requests very easily. With your server running, navigate to 'localhost:5001' in your browser.

The words "Hello World!" should appear on your screen.

Get Todos

Next, let's create some sample todos to use as data in our index.js.

// index.js

const express = require('express')
const app = express()
const port = 5001
const bodyParser = require('body-parser');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.listen(port, () => {
    console.log(`Node Todo API is running on port: ${port}`)
})

const todos = [
    { id: 1, text: "Brush teeth", completed: false },
    { id: 2, text: "Pet dog", completed: false },
    { id: 3, text: "Make Coffee", completed: false },
    { id: 4, text: "Write code", completed: false }

]


app.get('/', function (req, res) {
    return res.send("Hello World")
});


Enter fullscreen mode Exit fullscreen mode

And at the end of the index.js file we can add an event to listen to GET requests on the '/todos' route.

// index.js

...


app.get('/todos', function (req, res) {
    return res.send(todos)
});

Enter fullscreen mode Exit fullscreen mode

Now, when we go to the URL "localhost:5001/todos" in our browser to make the GET request, we should see an array of the todos from our index.js on our screen.

Get A Todo by ID

Let's add one more GET request. This request will return a single Todo object depending on what ID we send it in the request parameter.

// index.js

...

app.get('/todos/:id', function (req, res) {
    const id = req.params.id;
    let result = null
    for (let i = 0; i < todos.length; i++) {
        const todo = todos[i];
        if (todo.id == id) { // using == instead of === because id is a string.
            result = todo;
        }
    }
    return res.send(result);
});

Enter fullscreen mode Exit fullscreen mode

If we navigate to 'localhost:5001/todos/1', we should see our first todo in our browser.

The id variable in our code shows us how node.js can read from the request parameters and get the id property to use in our API.

Add a Todo with a POST request.

We have 2 routes that listen to GET requests and return either a list of all todos or a single todo by id.

Now, let's add our first POST request and add a todo to our list.

In index.js let's add the following route:

// index.js

...


app.post('/todos/', function (req, res) {
    const newId = todos.length + 1;
    const newTodo = {
        id: newId,
        todo: req.body.todo,
        completed: false
    }
    todos.push(newTodo)

    return res.send(todos);
});


Enter fullscreen mode Exit fullscreen mode

When we write a API, we want each item to have a unique ID. There is an npm package called uuid that works great for this, but for this simple project, I am just going to keep track of each todo by what order it is in, that is what the newId variable is doing. Also, each todo will start with a completed property that is set to false by default.

You will also see above, that the todo property is being set to the req.body.todo.

Let's talk more about what the request body, or req.body is.

req, res, req.body

Each node endpoint or route takes the route as the first variable ('/todos' in our examples). The second parameter in each endpoint is a callback function which takes the parameters req and res (it can also take other parameters but that is out of scope for this tutorial).

The req is the request object. The res is the response object.

Because these are parameters, they can be called anything you want, but req and res are the industry standard. It is the order, not the name, that matters.

The res is pretty simple. It is the response and many times you will use it to send the response back to the client (the consumer of this API.)

The req is more complicated and gets sent with potentially a lot of important and useful data that tells node information such as if a user is logged in or not.

In our example above, the req object can have a body property on it that sends POST requests useful information.

Our POST endpoint above shows that there is a body with a property of "todo" that is getting used to create the todo property on the variable newTodo.

When reading through a node API, you can learn a lot about what kind of properties to add to req.body so that you use the API correctly (although a good public facing API will have this documented.)

How to Test a POST Endpoint

To test a POST endpoint, developers use a tool called Postman. You can download it here.

Once it is downloaded, your request should look like this:

postman-req-example

After clicking the SEND button, you will get your response back. As you scroll through your response, you should see that the new todo has successfully gotten added to the last index of the list of all todos.

Postman can also be used to test GET, PUT, DELETE, PATCH, and other HTTP methods.

We were using our browser to test for GET requests earlier (a browser is basically just a fancy GET request making app). I will typically used Postman instead of my browser when testing GET requests.

Also, there are alternatives to Postman so feel free to search around and find something you like.

postman-res-example

Edit a Todo with a PUT request

As we progress in our ability to perform all CRUD processes, we have now arrived at the U part of CRUD, Update.

Adding the listener for a PUT request to our growing list of endpoints in our index.js will give us that updating ability.

Add this code to your index.js:

// index.js

..

app.put('/todos/', function (req, res) {

    //  Find the todo to update by ID

    let todoToUpdate = todos.find((todo) => {
        return todo.id == req.body.id
    })

    todoToUpdate = {
        id: req.body.id,
        todo: req.body.todo,
        completed: req.body.completed
    };


    //  Find the index of that todo to update.

    let index = todos.findIndex((todo) => {
        return todo.id == req.body.id
    });


    // Update the todo in the list

    todos[index] = todoToUpdate;


    //  Return the response

    return res.send(todos);
});
Enter fullscreen mode Exit fullscreen mode

The code above has a lot going on, so take some time if you need to in order to understand it.

We use an array method .find() to get the todo item we want to update from our array.

Then we update the item in our function.

After that we use .findIndex() to get the index of the list in the todos variable that we want to update.

Last we update the item in the actual array and return the response.

We are listening for a PUT request on the '/todos' route. Do you see what properties your request body needs for this endpoint?

It looks like we will need to send a request body with properties id, todo, and completed.

To test this in postman, lets use this as our request body.

{
    "id": 1,
    "todo": "Brush teeth",
    "completed":true
}
Enter fullscreen mode Exit fullscreen mode

The request body above will take the first todo in our list, and set completed from false to true.

After we test this out in Postman, we should see in our result that the first item in the array has completed set to true.

Delete a Todo

The last requirement in a CRUD application is the ability to delete an item.

Add this code to your index.js:

// index. js

...

app.delete('/todos/:id', function (req, res) {

    //  Find the index of that todo to update.
    let index = todos.findIndex((todo) => {
        return todo.id == req.params.id
    });

    todos.splice(index, 1);

    //  Return the response
    return res.send(todos);
});

Enter fullscreen mode Exit fullscreen mode

This is similar to our PUT request above. We use the array method .findIndex() to find the index of the item we want to delete, then we use .splice() to remove that one item.

You will also see that instead of sending any information in our req.body, this time we are only using the req.params and sending the item's id as a property on that object, similar to our endpoint where we get one todo from the list earlier.

Can you test this out in Postman?

If you make a DELETE request to 'localhost:5001/todos/1', you should get back an array of the original todos but without the first item.

Conclusion

We now have a working API built with node.js and express!

Congratulations, that's an achievement.

However, the honest truth is that this only scratches the surface of what these powerful tools can do. We do not yet have data persistence (using a database), any authentication, error handling, etc.

There are many more things that any REST API will typically need in a production environment to be ready for users

I will create more tutorials on these topics in the future, but what we have covered here is enough to get us started and learning more to create some powerful REST APIs with node.js and express.

Discussion (2)

Collapse
cmlandaeta profile image
cmlandaeta

Muy bueno para iniciar, me sirvió mucho para poder entender de que se trata el tema de API REST... Espero los siguientes proyectos para aprender más sobre esto.

Collapse
mwolfhoffman profile image
Michael Wolf Hoffman Author

Gracias!