Building a REST API with Node and Express

Introduction

REST APIs are an industry-standard way for web services to send and receive data. They use HTTP request methods to facilitate the request-response cycle and typically transfer data using JSON, and more rarely – HTML, XML and other formats.

In this guide, we are going to build a REST API to manage books with Node.js and Express.

For the sake of simplicity, we won’t be using a database, so you don’t need experience using one. We will use a simple JavaScript array to store our data instead.

What is a REST API?

REST (Representational State Transfer) is a standard architecture for building and communicating with web services. It typically mandates resources on the web are represented in a text format (like JSON, HTML, or XML) and can be accessed or modified by a predetermined set of operations. Given that we typically build REST APIs to leverage with HTTP instead of other protocols, these operations correspond to HTTP methods like GET, POST, or PUT.

On a collection of data, like books for example, there are a few actions we’ll need to perform frequently, which boil down to – Create, Read, Update and Delete (also known as CRUD Functionality).

An API (Application Programming Interface), as the name suggests, is an interface that defines the interaction between different software components. Web APIs define what requests can be made to a component (for example, an endpoint to get a list of books), how to make them (for example, a GET request), and their expected responses.

What is Express?

ExpressJS is one of the most popular HTTP server libraries for Node.js, which by default isn’t as friendly for API development. Using Express, we simplify API development by abstracting away the boilerplate needed to set up a server, which makes development faster, more readable and simpler. You can spin up a prototype API in seconds and a couple of lines of code.

Although it’s primary use was to simplify things with sensible defaults, it’s highly customizable using functions called “middleware”.

Note: Express is very lightweight and is built on top of middlleware. Using middleware, you can expand and extend its functionality beyond the functions already present by default.

Even though we are only going to build a REST API in this guide, the ExpressJS framework is not limited to just that – hosting static files, performing server-side rendering, or even using it as a proxy server isn’t uncommon and the sky’s the limit with additional middleware.

HTTP Request Types

There are a few types of HTTP methods that we need to grasp before building a REST API. These are the methods that correspond to the CRUD tasks:

  • POST: Used to submit data, typically used to create new entities or edit already existing entities.
  • GET: Used to request data from the server, typically used to read data.
  • PUT: Used to completely replace the resource with the submitted resource, typically used to update data.
  • DELETE: Used to delete an entity from the server.

Note: Notice that you can use either POST or PUT to edit stored data. You’re free to choose whether you even want to use PUT since it can be omitted fully. Though, stay consistent with the HTTP verbs you use. If you’re using POST to both create and update, then don’t use the PUT method at all.

What We are Going to Build

Let’s create a simple app to store information about books. In this app, we will store information about the ISBN of the book, title, author, published date, publisher and number of pages.

Naturally, the basic functionality of the API will be CRUD functionality. We’ll want to be able to send requests to it to create, read, update and delete Book entities. Of course, an API may do much more than this – provide users with an enpoint to get statistical data, summaries, call other APIs, etc.

Non-CRUD functionalities are application-dependent, and based on your project’s nature, you’ll probably have other endpoints. However, practically no project can go without CRUD.

To avoid making up book data – let’s use a dataset from GitHub to get some sample details about books.

Setting Up the Project

First, let’s initialize a new Node.js project:

npm init

Fill the requested information to your requirements – you don’t have to fill all of the fields, but they’re an easy way to set up identifiable data for a project. Fields like the name are much more relevant for publishing applications to the Node Package Manager, amongst other fields.

Alternatively, you can use the default settings by adding the -y flag to the call:

npm init -y

Either way – you’ll end up with a project with a package.json file. This is a json file that contains all the relevant metadata on your project and will look something along these lines by default:

{
  

"name"

:

"app"

,

"version"

:

"1.0.0"

,

"main"

:

"index.js"

,

"scripts"

: {

"test"

:

"echo \"Error: no test specified\" && exit 1"

},

"author"

:

""

,

"license"

:

"ISC"

,

"keywords"

: [],

"description"

:

""

}

The “entry”/”main” of your application is the file to run to start the project correctly – usually your main script, and index.js by default.

Additionally, the version of your application and “scripts” are here! You can supply any number of custom commands in the "scripts" section, with a command associated to an alias. Here, the test alias is a wrapper for an echo statement.

You’d run the test of the application via:

npm

test

[email protected]

test

/Users/david/Desktop/app

/Users/david/Desktop/app

echo

"Error: no test specified"

&&

exit

1 Error: no test specified

Oftentimes, there’s a start alias that masks one or more processes that should be run when we want to start an application. In the basic form – we just run the index page with node:

{
  

"name"

:

"app"

,

"version"

:

"1.0.0"

,

"main"

:

"index.js"

,

"scripts"

: {

"test"

:

"echo \"Error: no test specified\" && exit 1"

,

"start"

:

"node index.js"

},

"author"

:

""

,

"license"

:

"ISC"

,

"keywords"

: [],

"description"

:

""

}

You can put any number of commands besides node index.js as the start script and when you run npm start – they’ll all run:

test

npm start [email protected] start /Users/david/Desktop/app

node index.js

Note: Since we only have one command in the starting script, it’s functionally equivalent to just calling $ node index.js in the command line to start the application.

Now that you’re familiar with the project, let’s install Express!

npm install --save express

A new file is created in the directory, alongside a node_modules directory. The package-lock.json file keeps track of your dependencies and contains their versions and names:

{
  

"name"

:

"app"

,

"version"

:

"1.0.0"

,

"lockfileVersion"

:

1

,

"requires"

:

true

,

"dependencies"

: {

"accepts"

: {

"version"

:

"1.3.7"

,

"resolved"

:

"https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz"

,

"integrity"

:

"sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA=="

,

"requires"

: {

"mime-types"

:

"~2.1.24"

,

"negotiator"

:

"0.6.2"

} }, ...

The node_modules directory actually hosts the code of the dependencies, and can get quite large very quickly. Just by installing Express, we’ve already got a hefty number of modules installed and tracked in the package-lock.json file.

These modules are, factually, small so it’s not an issue by any means. By using the package-lock.json file, another client cann know which dependencies to download and which versions to use to correctly be able to start up your application.

Note: When doing version control with tools like Git – it’s considered a good practice not to version the source code of the modules you use in the application. In practical terms – don’t keep track or push node_modules to a repository. Others can download the dependencies based on the crucial package-lock.json which happens automatically when they run the application with npm.

Creating a Simple Endpoint

Now, let’s start building a simple “Hello World” app. It’ll have a single simple endpoint that just returns a message as a response to our request to get the home page.

First, let’s create a file called hello-world.js:

nano hello-world.js

Then, let’s import the Express framework within it:

const

express =

require

(

'express'

);

Next, we’ll want to instantiate the Express app:

const

app = express();

And set our port:

const

port =

3000

;

The port will be used a bit later, when we tell the app to listen to requests. These three lines are boilerplate – but the great thing is, that’s all the boilerplate there is!

Now, we can create a simple GET endpoint right beneath the boilerplate. When a user hits the endpoint with a GET request, the message “Hello World, from express” will be returned (and rendered in the browser or displayed on the console).

We’d like to set it to be on the home page, so the URL for the endpoint is /:

app.get(

'/'

,

(

req, res

) => { res.send(

'Hello World, from express'

); });

At this point, let’s start our clients:

app.listen(port, 

() =>

console

.log(

`Hello world app listening on port

${port}

!`))

Let’s run the application and visit the only endpoint we have via our browser:

node hello-world.js

Hello world app listening on port 3000!

node js rest api endpoint result

This is technically a working API! Though, this endpoint doesn’t really do much. Let’s take a look at some common middleware that’ll be useful for further work and create some more useful endpoints.

Express Middleware

As mentioned above – ExpressJS is a simple HTTP server and it does not come with a lot of features out of the box. Middleware act almost like extensions for the Express server and provide additional functionalities in the “middle” of a request. Many third-party extenssions like morgan for logging, multer for handling file uploads, are used routinely.

For now, to get started, we need to install a middleware called body-parser, which helps us decode the body from an HTTP request:

npm install --save body-parser

It parses the body of the request and lets us react to it accordingly.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Since we are calling the API from different locations by hitting endpoints in the browser. We also have to install the CORS middleware.

If you’re not yet familiar with cross-origin resource sharing, it is okay for now. Let’s just install the middleware and configure it:

npm install --save cors

Building a REST API with Node and Express

Adding Books

Now we can start building our app. Create a new file called book-api.js:

const

express =

require

(

'express'

)

const

bodyParser =

require

(

'body-parser'

);

const

cors =

require

(

'cors'

);

const

app = express();

const

port =

3000

;

let

books = []; app.use(cors()); app.use(bodyParser.urlencoded({

extended

:

false

})); app.use(bodyParser.json()); app.post(

'/book'

,

(

req, res

) => { }); app.listen(port,

() =>

console

.log(

`Hello world app listening on port

${port}

!`));

As you can see, we can configure body-parser by importing it and passing it to the app.use method, which enables it as middleware to the Express app instance.

We will be using the books array to store our collection of books, simulating a database.

There are a few types of HTTP request body types. For an example, application/x-www-form-urlencoded is the default body type for forms, whereas application/json is something we’d use when requesting a resource using jQuery or backend REST client.

What the body-parser middleware will be doing is grabbing the HTTP body, decoding the information, and appending it to the req.body. From there, we can easily retrieve the information from the form – in our case, a book’s information.

Inside the app.post method let’s add the book to the book array:

app.post(

'/book'

,

(

req, res

) => {

const

book = req.body;

console

.log(book); books.push(book); res.send(

'Book is added to the database'

); });

Now, let’s create a simple HTML form with the fields: ISBN, title, author, published date, publisher, and number of pages in a new file, say new-book.html.

We’ll be sending the data to the API using this HTML form’s action attribute:

<

div

class

=

"container"

>

<

hr

>

<

h1

>Create New Book

</

h1

>

<

hr

>

<

form

action

=

"http://localhost:3000/book"

method

=

"POST"

>

<

div

class

=

"form-group"

>

<

label

for

=

"ISBN"

>ISBN

</

label

>

<

input

class

=

"form-control"

name

=

"isbn"

>

</

div

>

<

div

class

=

"form-group"

>

<

label

for

=

"Title"

>Title

</

label

>

<

input

class

=

"form-control"

name

=

"title"

>

</

div

>

<

button

type

=

"submit"

class

=

"btn btn-primary"

>Submit

</

button

>

</

form

>

</

div

>

Here, our <form> tag’s attribute corresponds to our endpoint and the information we send with the submit button is the information our method parses and adds to the array. Note that the method parameter is POST, just like in our API.

You should see something like that when you open the page:

node js rest api form

Clicking “Submit”, we’re greeted with the our applications console.log(book) statement:

{ 

isbn

:

'9781593275846'

,

title

:

'Eloquent JavaScript, Second Edition'

,

author

:

'Marijn Haverbeke'

,

publish_date

:

'2014-12-14'

,

publisher

:

'No Starch Press'

,

numOfPages

:

'472'

}

Note: Please note that since we are using an array to store data we will lose them in our next app restart.

Getting All Books

Now let’s create an endpoint to get all the books from the API:

app.get(

'/books'

,

(

req, res

) => { res.json(books); });

Restart the server. If the server is already running press Ctrl + C to stop it first. Add some books and open http://localhost:3000/books in your browser. You should see a JSON response with all the books that you’ve added.

Now let’s create an HTML page to display these books in a user-friendly way.

This time around, we’ll create two files – book-list.html which we’ll use as a template and a book-list.js file which will hold the logic to updating/deleting books and displaying them on the page:

Let’s start off with the template:

<

div

class

=

"container"

>

<

hr

>

<

h1

>List of books

</

h1

>

<

hr

>

<

div

>

<

div

class

=

"row"

id

=

"books"

>

</

div

>

</

div

>

</

div

>

<

div

id

=

"editBookModal"

class

=

"modal"

tabindex

=

"-1"

role

=

"dialog"

>

<

div

class

=

"modal-dialog"

role

=

"document"

>

<

div

class

=

"modal-content"

>

<

div

class

=

"modal-header"

>

<

h5

class

=

"modal-title"

>Edit Book

</

h5

>

<

button

type

=

"button"

class

=

"close"

data-dismiss

=

"modal"

aria-label

=

"Close"

>

<

span

aria-hidden

=

"true"

>

&times;

</

span

>

</

button

>

</

div

>

<

div

class

=

"modal-body"

>

<

form

id

=

"editForm"

method

=

"POST"

>

<

div

class

=

"form-group"

>

<

label

for

=

"ISBN"

>ISBN

</

label

>

<

input

class

=

"form-control"

name

=

"isbn"

id

=

"isbn"

>

</

div

>

<

div

class

=

"form-group"

>

<

label

for

=

"Title"

>Title

</

label

>

<

input

class

=

"form-control"

name

=

"title"

id

=

"title"

>

</

div

>

<

button

type

=

"submit"

class

=

"btn btn-primary"

>Submit

</

button

>

</

form

>

</

div

>

</

div

>

</

div

>

</

div

>

<

script

src

=

"book-list.js"

>

</

script

>

With the template done, we can implement the actual logic to retrieve all books using browser-side JavaScript and our REST API:

const

setEditModal =

(

isbn

) => { }

const

deleteBook =

(

isbn

) => { }

const

loadBooks =

() =>

{

const

xhttp =

new

XMLHttpRequest(); xhttp.open(

"GET"

,

"http://localhost:3000/books"

,

false

); xhttp.send();

const

books =

JSON

.parse(xhttp.responseText);

for

(

let

book

of

books) {

const

x =

` <div class="col-4"> <div class="card"> <div class="card-body"> <h5 class="card-title">

${book.title}

</h5> <h6 class="card-subtitle mb-2 text-muted">

${book.isbn}

</h6> <div>Author:

${book.author}

</div> <div>Publisher:

${book.publisher}

</div> <div>Number Of Pages:

${book.numOfPages}

</div> <hr> <button type="button" class="btn btn-danger">Delete</button> <button types="button" class="btn btn-primary" data-toggle="modal" data-target="#editBookModal" onClick="setEditModal(

${book.isbn}

)"> Edit </button> </div> </div> </div> `

document

.getElementById(

'books'

).innerHTML =

document

.getElementById(

'books'

).innerHTML + x; } } loadBooks();

In the above script, we are sending a GET request to the endpoint http://localhost:3000/books to retrieve the books and then creating a Bootstrap card for every book to display it. If everything is working correctly you should see something like this on your page:

node js rest api book list

You probably noticed the Edit and Create buttons and their respective methods. For now, let’s leave them empty and implement them as we go.

Retrieving a Book by ISBN

If we’d like to display a specific book to the user, we’ll need a way to retrieve it from the database (or the array, in our case). This is always done by a key specific to that entity. In most cases, each entity has a unique id that helps us identify them.

In our case, each book has an ISBN which is unique by nature, so there’s no need for another id value.

This is typically done by parsing the URL parameter for an id and searching for the book with the corresponding id.

For an example, if the ISBN is 9781593275846 the URL would look like, http://localhost:3000/book/9781593275846:

app.get(

'/book/:isbn'

,

(

req, res

) => {

const

isbn = req.params.isbn; });

Here, we’re introduced to parametrized URLs. Since the ISBN depends on the book, there’s potentially an infinite number of endpoints here. By adding a colon (:) to the path, we can define a variable, mapped to the variable isbn. So, if a user visits localhost:3000/book/5 the isbn parameter will be 5.

You can accept more than one parameter in your URL if it makes sense in your scenario. For example /image/:width/:height, and then you can get those parameters using req.params.width and req.params.height.

Now, using our endpoint, we can retrieve a single book:

app.get(

'/book/:isbn'

,

(

req, res

) => {

const

isbn = req.params.isbn;

for

(

let

book

of

books) {

if

(book.isbn === isbn) { res.json(book);

return

; } } res.status(

404

).send(

'Book not found'

); });

Again restart the server, add a new book, and open localhost/3000/{your_isbn} and the application will return the book’s information.

Deleting Books

When deleting entities, we typically delete them one by one to avoid big accidental data loss. To delete items, we use the HTTP DELETE method and specify a book using its ISBN number, just like how we retrieved it:

app.delete(

'/book/:isbn'

,

(

req, res

) => {

const

isbn = req.params.isbn; books = books.filter(

i

=> {

if

(i.isbn !== isbn) {

return

true

; }

return

false

; }); res.send(

'Book is deleted'

); });

We are using the app.delete method to accept DELETE requests. We have also used the array filter method to filter out the book with the relevant ISBN to remove it from the array.

Now let’s implement the deleteBook method in the book-list.js file:

const

deleteBook =

(

isbn

) => {

const

xhttp =

new

XMLHttpRequest(); xhttp.open(

"DELETE"

,

`http://localhost:3000/book/

${isbn}

`,

false

); xhttp.send(); location.reload(); }

In this method, we are sending the delete request when the button is pressed and reloading the page to display the changes.

Editing Books

Very similar to deleting entities, updating them requires us to snatch a specific one, based on the ISBN and then send either a POST or PUT HTTP call with the new information.

Let’s go back to our book-api.js file:

app.post(

'/book/:isbn'

,

(

req, res

) => {

const

isbn = req.params.isbn;

const

newBook = req.body;

for

(

let

i =

0

; i < books.length; i++) {

let

book = books[i]

if

(book.isbn === isbn) { books[i] = newBook; } } res.send(

'Book is edited'

); });

Upon sending a POST request, aimed at a specific ISBN, the adequate book is updated with new information.

Since we have already created the edit modal, we can use the setEditModal method to gather information about the book when “Edit” button is clicked.

We will also set the form’s action parameter with the clicked book’s URL to send the request:

const

setEditModal =

(

isbn

) => {

const

xhttp =

new

XMLHttpRequest(); xhttp.open(

"GET"

,

`http://localhost:3000/book/

${isbn}

`,

false

); xhttp.send();

const

book =

JSON

.parse(xhttp.responseText);

const

{ title, author, publisher, publish_date, numOfPages } = book;

document

.getElementById(

'isbn'

).value = isbn;

document

.getElementById(

'title'

).value = title;

document

.getElementById(

'author'

).value = author;

document

.getElementById(

'publisher'

).value = publisher;

document

.getElementById(

'publish_date'

).value = publish_date;

document

.getElementById(

'numOfPages'

).value = numOfPages;

document

.getElementById(

'editForm'

).action =

`http://localhost:3000/book/

${isbn}

`; }

To verify if the update function works, edit a book. The form should be filled with the existing information about the book. Change something and click “Submit” after which you should see a “Book is edited” message.

Conclusion

That’s how easy it is to build a REST API using Node.js and Express. You can visit the official Express documentation to learn more about the framework if you are interested.

Also, the code that I have provided is just for the sake of the tutorial, you should never use it in a production environment. Make sure you validate data and follow best practices when you write code for production.

As usual, the source code of this project can be found on GitHub.