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)
You will also need these system requirements:
- Node.js installed. You can install it here
- NPM installed. (Should be installed with node above, or here)
- An editor that you like working in. I use VS Code.
- A terminal you enjoy using such as cmd, powershell, or cmder
- 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
- 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
- Navigate into your new directory
cd mkdir todo-api-node-js
- Use this command to create a new node.js project.
npm init
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"
}
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
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"
}
}
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
Lets open up the .gitignore
file and simply add the text node_modules
in there.
// .gitignore
node_modules
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"
}
}
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"
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
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!")
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!")
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
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}`)
})
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');
...
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}`)
})
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!")
});
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!"
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")
});
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)
});
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);
});
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);
});
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:
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.
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);
});
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
}
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);
});
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.
Top comments (3)
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.
Gracias!
Beautiful Article Hoffman! This is indeed the most relatable I’ve seen yet.