Presented in 2009 and released in 2010, the Node.js platform began to attract the interest of web developers from all over the world as an ideal solution for implementing the backend of applications. Already used by companies like LinkedIn, Uber, eBay, and PayPal, Node’s non-blocking and event-driven architecture lets you build very performant and scalable network services fast due to its flat learning curve and available tools like Express.

Express.js is a minimalistic but very powerful framework for the smooth development of server-side logic and APIs with Node. In existence for over 10 years, it is today practically the standard choice for backend developers.

In this article, you’ll learn how to make troubleshooting Express applications as quick and effective as the Express development process itself.

Express Framework

Node applications created using Express can run in almost any environment, including simple laptops, virtual machines, PaaS solutions like AWS Elastic Beanstalk, or even serverless AWS Lambda.

Express effectively utilizes the “middleware” design pattern for handling the request-response lifecycle. Middleware consists of reusable functions that can access the request and response objects, mutate them, perform side effects, and then pass them on to the next middleware or simply break the chain with an error (e.g., unauthorized user request).

Developers may simply define the endpoints, attach one or multiple middleware, write the controllers (functions responsible for executing the business logic and sending responses back to the client), and that’s it—the application is literally done!

Monitoring and Troubleshooting

As with any other kind of software, you need to maintain and observe Express applications so engineers can react to any potential issues quickly. Developers used to write a logger-middleware, place it in front of the root endpoint, and then use the collected log records to debug and troubleshoot. Although such an approach may be good enough, it has a couple of drawbacks:

  • Without additional mechanisms, you get no additional information like detailed timings of a particular request or issues with third-party services.
  • You need to manage logs and implement custom solutions for storing, structuring, searching, analyzing, and visualizing logged data.

In short, this approach takes a considerable effort, time, and resources. Fortunately, Epsagon offers a powerful alternative.

Epsagon to the Rescue

Epsagon provides complex tooling for monitoring and troubleshooting Node.js applications; plus, it’s infrastructure-agnostic and very easy to integrate into both existing and new projects. Epsagon automatically captures all inbound and outbound requests, errors, and other important operational details. Developers can also add tags, create custom errors, and specify other data to be captured. All traced data is stored in the Epsagon system and made available for querying and analysis.

In order to better understand Epsagon’s capabilities when using Express, let’s create and deploy a very basic web application.

Example Project Description

The application you’ll be creating will calculate the distance between given geographical coordinates and the coordinates of the International Space Station. The latter can be found here, while the distance calculations will be implemented with JavaScript. You will also need to have Node.js installed on your computer.

The application will have the following endpoints:

  • GET /hc: Returns an empty response with status code 200, meaning the app is up and running normally (usually called a “health check”)
  • GET /iss: Returns the coordinates of the current ISS position
  • POST /iss/distance: Receives the “latitude” and “longitude” parameters and returns the distance in kilometers

Requests sent to any other endpoint will get a “Not Found” response with the status code 404.

So, let’s begin!

Coding the Application

First, create a directory for your project, open the terminal, navigate to the new directory, and run the following command:

npm init -y

 

To install the Express framework and the module named “got” (a very human-friendly library for making external HTTP requests), run:

npm install express got

 

After this, you should see a “package.json” file in the directory with all the dependencies listed inside.

Now, to create a basic Express server with the “/hc” and “/iss” endpoints, you need an “index.js” file with the following content:

const express = require('express');
const got = require('got');
const ISS_URL = 'http://api.open-notify.org/iss-now.json';

const app = express();
app.use(express.json());

app.get('/hc', (req, res) => {
 res.sendStatus(200);
});

app.get('/iss', async (req, res, next) => {
 const issInfo = await got('ISS_URL').json().catch(next);
 res.json(issInfo);
});

app.use((req, res, next) => {
 const error = new Error('Not Found');
 error.status = 404;
 next(error);
});

app.use((error, req, res, next) => {
 res.status(error.status || 500);
 res.send(error.message || 'Something went wrong!');
})

const port = 3000;
app.listen(port, () => {
 console.log(`Application listening at port: ${port}`);
});

 

Open the terminal within the project directory, and run the command below to start the server:

node index

 

Now, open the web browser, and try navigating to http://localhost:3000/hc and http://localhost:3000/iss. You should see the word “OK” and the space station data. Press CTRL + C together to stop the server.

Next, it’s time to add the main feature of the app—the ability to pass any coordinates as an input and get the distance to the ISS projection point!

Create a new file named “calculate.js” with the following code inside:

const EARTH_RADIUS = 6371;

function toRad(degrees) {
 return degrees * Math.PI / 180;
}

function calculate(coords1 = {}, coords2 = {}) {
 let { latitude: lat1 , longitude: lon1 } = coords1;
 let { latitude: lat2 , longitude: lon2 } = coords2;

 if (isNaN(lat1) || isNaN(lat2) || isNaN(lon1) || isNaN(lon2)) {
   throw new Error('Invalid Input');
 }

 const dLat = toRad(lat2-lat1);
 const dLon = toRad(lon2-lon1);

 lat1 = toRad(lat1);
 lat2 = toRad(lat2);

 const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
         Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
 const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

 return EARTH_RADIUS * c;
}

module.exports = calculate;

-----------------------------------------------
const express = require('express');
const got = require('got');
const calculate = require('./calculate'); // new require

const ISS_URL = 'http://api.open-notify.org/iss-now.json';

const app = express();
app.use(express.json());

app.get('/hc', (req, res) => {
 res.sendStatus(200);
});

app.get('/iss', async (req, res, next) => {
 const issInfo = await got(ISS_URL).json().catch(next);
 res.json(issInfo);
});

app.post('/iss/distance', async (req, res, next) => { // new endpoint
 try {
   const issInfo = await got(ISS_URL).json().catch(next);
   const { iss_position } = issInfo;
   const result = { distance: calculate(req.body, iss_position) }
   res.json(result);
 } catch (err) {
   err.status = 400;
   next(err);
 }
});

app.use((req, res, next) => {
 const error = new Error('Not Found');
 error.status = 404;
 next(error);
});

app.use((error, req, res, next) => {
 res.status(error.status || 500);
 res.send(error.message || 'Something went wrong!');
})

const port = 3000;
app.listen(port, () => {
 console.log(`Application listening at port: ${port}`);
});

These smart calculations use some basic trigonometry to obtain the distance between points on a sphere. Earth is not an ideal sphere, but an ellipsoid; however, the above example is accurate enough for most use cases. Now, you need to “require” (import) the “calculate” function into the main file, add a new endpoint for handling POST requests, and use this function in the new endpoint handler. Above is the full code after changes (additions are marked in comments).

Run the application:

node index

 

And then in a separate terminal window, execute the following command to make a request to your server. Try out any coordinates you like:

curl -d '{"latitude": 49.8241, "longitude": 23.9743}' -H "Content-Type: application/json" -X POST http://localhost:3000/iss/distance

 

You should see the result immediately. Run the same command a few times and see how distance increases or decreases, depending on how the entered coordinates are related to the position of the space station.

Figure 1: Results of multiple requests

Congratulations, your Express application is ready!

Connecting Epsagon

For this part, you’ll first need to create an Epsagon account if you don’t already have one, then go to the “settings” page and copy the token, which you’ll need in just a minute. Now, navigate to the project directory and install the “epsagon-frameworks” module:

npm install epsagon-frameworks

 

Next, open the index.js file and enter the following code at the top of the file content, including your token:

const epsagon = require('epsagon-frameworks');

epsagon.init({
 token: 'YOUR-EPSAGON-TOKEN',
 appName: 'app-iss-distance-dev',
 metadataOnly: false,
});

 

The cool thing about Epsagon is that this code is enough for Epsagon to start working and collecting traces of your application! However, there are a few other ways you can enable Epsagon, as described in its documentation.

Traces List & Search

For this feature, restart the server, make a few requests to the “/iss” and “/iss/distance” endpoints, and, finally, open the “Trace Search” page in the Epsagon app.

Figure 2: Trace list

As you can see, every single request and general overview is visible on this page, taking the troubleshooting of Express or any other Node.js application to a new level. Developers don’t need to dig through tons of unsorted logs (if they exist at all); instead, they can use smart search and filters to find a particular event that caused an issue and investigate its related data. You can also select any item in the list to view detailed information.

Figure 3: Trace details

Tagging Traces

Epsagon lets you mark traces or add specific data to the trace by using the “trace tags” feature. This allows developers to use custom-labeled data in further trace searches and data aggregation to improve troubleshooting processes or build data visualizations for useful insights. For example, let’s add a custom label for tracking the resulting distances sent to users. In the “index.js” file, just before sending the result to the user, add a single line:

app.post('/iss/distance', async (req, res, next) => {
 try {
   const issInfo = await got(ISS_URL).json().catch(next);
   const { iss_position } = issInfo;
   const result = { distance: calculate(req.body, iss_position) }
   epsagon.label('distance', result.distance); // <-- this one
   res.json(result);
 } catch (err) {
   err.status = 400;
   next(err);
 }
});

 

Restart the server, and make another bunch of requests to the POST /iss/distance endpoint. Now, go to the trace search page, and select the filter for distance. As you can see below, per the custom label given, only traces where the resulting distance is less than 1,000 km will be displayed.

Figure 4: Filter traces by tag

Visualizations

Since engineers now have a lot of useful data for troubleshooting Express applications, it makes sense to create a dashboard with visualizations of real-time and aggregated data. This will improve the general observability of your application and even decrease issues since engineers can more easily notice suspicious trends or values.

Open the Dashboards list, then click the “+” button and give a name to your new dashboard. Now, you can click the “add panel” button to open the query and visualizations editor, a very flexible and easy-to-use tool for creating custom charts. In the chart created below, you can see the average distance received by users as a result of their requests.

Figure 5: Creating a custom chart

You can even add multiple queries to the same graph and customize the colors, descriptions, and other settings for each metric.

Further Success of Your Project

In this article, you learned the core concepts of troubleshooting applications written in Node.js and built on top of the Express framework. However, implementing a perfect tracing solution is not the only key to having truly production-ready applications. You need to properly design your API routes, middleware, and controllers; consider authorization flow, user roles, and access control mechanisms; and plan which third-party services and APIs you’ll be using, taking their cost into account.

There are many other factors you need to consider as well when embarking on a project: proper infrastructure, security, CI/CD processes, etc., each with its own features and technical aspects. Good luck with your next or existing project!

To take the next step, start your Epsagon 14-day free trial.

Read More:

Getting started with AWS Lambda and Express

How to troubleshoot API errors

Troubleshooting application errors with Epsagon