Pre-Requisites

Sprucing up HTML with CSS

Assignment Summary

One of the ways to store data for a web application so that it can be accessed from multiple computers is to store the data on a server and create an HTTP (HyperText Transfer Protocol) REST (Representational state transfer) API (Application Programming Interface)

In this assignment, we will use NodeJS to create a simple web server. We will use a very common web framework called express to simplify the code that is written.

You will learn how to make CRUD (Create, Read, Update, and Delete) routes for our notes data.

At the end of the assignment, you will have a REST server that stores data on the server that can be accessed by making HTTP requests to the server.

Installing NodeJS

NVM is my preferred way to install node on my system.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash

You likely need to close and re-open your terminal to ensure it can access the newly install nvm command.

nvm install node

Now you can confirm node is installed by running node -v.

Initializing Project

Create a new directory called api-intro.

mkdir -p ~/code/api-intro

Change directory into this folder.

cd ~/code/api-intro

Create npm project.

npm is a package manager for JavaScript. It enables installing and managing dependencies for your project.

npm init

The default values are fine for our purposes so you can just keep hitting enter until it exits. This command generates a package.json file that stores configuration of the project.

Set type to module in package.json

{
  "name": "@engramhq/blog",
+ "type": "module", 
  "version": "1.0.0",
  "main": "index.js",
  "repository": "git@github.com:engramhq/blog.git",
  "author": "Adam Berg <adam@xyzdigital.com>",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2"
  }
}

Unfortunately, JavaScript has gone through some changes that has left multiple different ways of doing things. This StackOverflow question has some decent context to explain the difference between “CommonJS” and “ES6 Modules”.

The primary difference is how code is imported and exported between files. You’ll see many examples when searching for Node.JS solutions that use require instead of import. As far as I can tell, the import syntax should be preferred.

Create Express Application

express is a very common javascript package that simplifies the creation of an HTTP server.

Install express Package

npm i express

Create index.js File

// index.js
// imports the library using ES6 Module syntax
import express from "express";

// Creates a new express application
const app = express();

// Configures express to handle GET requests to "/"
app.get("/", (req, res) => {
  // Specifies the data to return to the browser for the specified route
  res.send("Hello World");
});

// Tells express to open the server on port 8080
app.listen(8080);

Start the server:

node index.js

Test

Open a brower to http://localhost:8080 and you should see the “Hello World” message.

Create, Read, Update, Delete (CRUD)

CRUD are the four basic operations of persistent storage. We will add each of these operations one by one and, once complete, we will have a working API that persists data on the server.

REST defines several HTTP request methods. These are not necessarily mandatory to use, but over time it will become more clear why they exist.

CRUD to method mappings

   
CRUD HTTP
Create POST, PUT if we have id or uuid
Read GET
Update PUT
Delete DELETE

So far, our web application only needs Create and Read so we will begin with just these.

Create

// This enables JSON parsing of the request body
app.use(express.json());

// This variable will hold our notes
const notes = [];

// This defines a route on http://localhost:8000/notes for the POST method
app.post("/notes", (req, res) => {
  // The `req.body` property will have the data that was passed from the browser
  notes.push(req.body);

  console.log(notes);

  // HTTP uses Status Codes (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
  // to identify whether a request has been successfully completed
  // 200 specifies that the operation was a success
  res.sendStatus(200);
});

Restart the server

If you left the server running from the first step, you can stop it by focusing on the terminal (e.g. clicking into it) and then hitting ctrl+c on you keyboard. This will stop the currently running command.

node index.js

Test

There are many ways to test a REST API. curl is an extremely popular command line client that is worth getting familiar with. The command below constructs an HTTP POST request with a JSON body of:

{
  "body": "Hello World"
}
curl -X POST -H "Content-Type: application/json" -d '{"body": "Hello World"}' http://localhost:8000/notes

The -X POST sets the HTTP method to “POST” (the default is “GET”).

The -H "Content-Type: application/json" sets the “Content-Type” header to “application/json” which informs the server on how to parse this information.

The -d '{"body": "Hello World"}' attaches this data to the body of the request (this is what req.body will be set to).

The http://localhost:8000/notes portion specifies the URI that we are making the request to. This maps directly to the “/notes” route that we defined on this lin: app.post("/notes", (req, res) => {.

If successful, you should see OK(base) after the curl command and in the logs of your node server, you should see:

[ { body: 'Hello World' } ]

Read

Now that we have a route that can add notes, we need a new route to return the existing notes. This will eventually be used by the client side browser application to load all existing notes when opening the web application.

app.get("/notes", (req, res) => {
  // res.json handles converting the passed object to JSON and setting appropriate headers
  // It will also automatically set the HTTP status code to 200
  res.json(notes);
});

Automatically reload your server

Every time you make a change to the index.js file, you need to reload it. You can automate this process by running the server with:

node --watch index.js

This will automatically restart the process whenever the index.js file changes.

Test

This one can be tested in the browser easily by navigating to http://localhost:8000/notes. Any notes created will get destroyed when the server restarts, so you will need to re-run the curl command from the previous section to add notes and confirm they are returned correctly. The next assignment will introduce a database to prevent this from being a problem.

Final Solution

// index.js
import express from "express";

const app = express();

app.use(express.json());

app.get("/", (req, res) => {
  res.send("Hello World");
});

const notes = [];

app.post("/notes", (req, res) => {
  notes.push(req.body);

  res.sendStatus(200);
});

app.get("/notes", (req, res) => {
  res.json(notes);
});

app.listen(8000);

Next

In the next assignment, we will hook up our existing frontend web application with these new backend REST API routes.

Connecting to REST API to Persist Data Between Browser Sessions