Express Server Side Applications

Express is a server-side Javascript library that helps in writing web server applications. This chapter will introduce the very basics of using Express to write a server application that implements a JSON API.

Running Javascript with Node.js

On the client-side we operate in a web browser environment that provides a built-in Javascript engine. On the server side we need to find a suitable Javascript run-time environment; this is provided by Node.js. Node.js runs almost the same variety of Javascript as a modern browser and has additional libraries that implement things like file-system access etc. - things that aren't allowed in the browser for security reasons. Once you install Node.js, you can run a Javascript source file with the command:

node index.js

NPM: the Node Package Manager

NPM is a package manager installed with Node.js that handles downloading and installing third-party packages for your projects. When you install a package using the npm command it is installed by default in the project directory into a folder called node_modules. This means that the packages that you have installed can be different for different projects. There is a way to install things globally, but that is much less common in the Javascript world.

We'll see some examples of installing packages later in this chapter.

Initialising a Node Project

When you first start a project with Node.js you will run the npm init command to create a file called package.json. This will contain the configuration of your project including a list of the third-party packages that it relies on. Having answered the questions, it will create a new package.json file something like this:

{
  "name": "server-example",
  "version": "1.0.0",
  "description": "An example json web api",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Steve Cassidy",
  "license": "ISC",
}

This file describes your project and can be used to hold any configuration for various tools you might use in development.

In this project, we'll make use of the express module to write our web server application. This is a third-party module and we can install it into our project with the npm command:

npm install express

This does two things: it downloads both express and any modules that it depends and installs them into the node_modules directory in your project, and, it updates your package.json file to record the dependency on this package. It will add something like the following to your package.json:

  "dependencies": {
    "express": "^4.18.2"
  }

This records the name and version number of the module it has installed. This means that someone else using your code can reproduce the exact dependencies for your project, just by running npm install.

Installation also creates a file package-lock.json which has much more detail about the packages installed and any dependent packages. Both of these files should be kept as part of your project and added to your git repository.

The node_modules directory should not be checked in to your git project as it is often very large and can be reproduced exactly from the package-lock.json file. To prevent this being added to the project accidentally, we create a file .gitignore which lists files and directories that should be ignored by Git:

# ignore the node_modules folder
node_modules/

At this point, you might want to initialise your git repository for the project and commit these files.

A Simple Server

We'll now move on to writing a simple application server using Express. The application will implement a simple collection of strings (things I like) and have endpoint URLs to get the collection (GET) and create a new entry (POST). This won't use a database, just an in-memory array of strings for storage.

We'll write our Express application in the file index.js and to being with, it will look like this:

const express = require('express');

const app = express();

// more code will go here

const PORT = 3123;
console.log(`Application is listening on http://localhost:${PORT}/`);
app.listen(PORT);

This code first imports the express module using a different syntax to the one used in earlier front-end code. This is an older module standard implemented by the Node.js system before there was any such thing in the standard. It is possible to use modern import express from 'express' syntax in Node but it takes a little bit of configuration. We use this syntax here because it is much more widespread in examples of server-side programming that you'll see on the web.

The next line of code creates a new express application that can accept requests and generate responses. By default, it doesn't know how to respond to any requests but we'll get to that. THe last three lines start the server running - printing out a message and then calling app.listen to start monitoring the given port for requests.

You can run this from the command line by typing:

node index.js

You should see the message and then it will sit there waiting for requests. Go to your browser and enter http://localhost:3123 and you should see something like Can't GET /. This means that the server is working, but that we have not yet told it what to do when a request arrives.

We'll write a request handler as follows:

app.get('/', (req, res) => {
    res.json({hello: 'world'});
});

(this goes into the index.js file where the 'more code' comment is above). This code defines a handler for GET requests to the URL '/' by associating a function (the second argument) with a URL pattern. The handler function will be called if there is a request matching this URL and will be passed two objects: req is the request object, res is the response. The function can get information from the request and update the response that it wants to return.

In this example, the handler just gives the response a JSON payload. This will have the effect of setting the Content-Type for the response to application/json and adding the encoded JSON string to the response body.

You'll need to stop and re-start your server for the new code to be seen. Use Ctrl-C to stop it running.

If you go back to your browser and refresh the page you should now see the JSON response in the page. Look at the request in your browser tools and you'll see the headers that have been set by the server, something like this:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 17
ETag: W/"11-IkjuL6CqqtmReFMfkkvwC0sKj04"
Date: Fri, 05 May 2023 06:18:57 GMT
Connection: keep-alive
Keep-Alive: timeout=5

We didn't need to write much code to get this functionality. The Express module is doing the work for us of creating the right HTTP response headers.

Server Restarts - nodemon

Restarting the server every time we make a change is a pain. Luckily other people think the same and there are options for automatically re-starting if you edit a file in the project. The most common option is a package called nodemon which behaves just like node but will monitor for changes in your code and restart if it sees any. We'll install nodemon but we'll save it as a development dependancy only:

npm install --save-dev nodemon

This will install the package in node_modules and update the package.json with something like:

  "devDependencies": {
    "nodemon": "^2.0.22"
  }

Adding Data

This application doesn't do much yet so let's add the list of likes as described and write the handlers for the /likes URL. As mentioned, our data store is a global variable and a GET request to /likes retrieves the contents of the data store.

const data = {
    likes: [],
};

app.get('/likes', (req, res) => {
    res.json(data.likes);
});

Once this code is in place, we can send a request to http://localhost:3123/likes and get a response of [] - the empty array, since there is nothing stored in the global variable yet. So, let's now implement the handler for a POST request to the same URL that will accept a JSON body containing something we like and add it to the array.

The POST request will accept a JSON body like this:

{
    "thing": "cheese"
}

To support parsing JSON in a request we first need to add some middleware to our application. Middleware in Express is a function that runs before or after a request handler that can modify the request in some way. In this case, the middleware will parse the JSON payload (if the Content-Type header is application/json) and make it available via the request object. The following line of code should go just after the app object is created:

app.use(express.json());

With this in place, we can now write the POST handler:

app.post('/likes', (req, res) => {
    if( 'thing' in req.body) {
        data.likes.push(req.body.thing);
        res.json({status: 'success'});
    }  else {
        res.json({status: 'error', message: 'missing thing'});
    }
});

This handler function is created using app.post (rather than app.get) so it will handle POST requests. It will match any request for /likes. Inside the handler function, req is the request object and req.body is the result of parsing the JSON payload of the request if there was one. We check for a property thing in the body and if it is there, we push it into the global data array, data.likes.

The response is a JSON object with a status field that could be used by the sender of the request to find out if everything went well.

We can now send a POST request to the server to add something to our list of likes. Of course, we can't do this directly with the browser, we'd need to write some front-end code to make use of this simple API. Alternately, you can use a tool like RESTED for Chrome or for Firefox to create test requests in your browser (RESTED is just one of these, there are others to choose from).

Summary

This has been a very simple example of a server-side application using Express that implements a JSON API. It uses an in-memory data store to keep a list of things and returns the current list in response to a GET request.

There is obviously much more to a real web application, the first big thing being persistent storage. This would be in a database of some kind - either a traditional SQL database or a NoSQL database like MongoDB or CouchDB that stores JSON data directly.