Building a COVID19 Dashboard using Node.js
Tags: node, tutorial, opensource, octograd2020
Published: 2020-05-16 15:36:06

### Hey there! My name is Julian.
I am one of the core-maintainers of the [NovelCOVID API](https://disease.sh), which is an open-source API for COVID19 related data. 
In this article, I would like to show you how easy it is to build a COVID19 dashboard using our API. 
A few weeks ago, I built [Covid Detail](https://coviddetail.com), [Covid Overview](https://covidoverview.com) and [Covid19 Daily Digest](https://covid19dailydigest.com) with the data provided by the API.

This guide is for people that know a bit about coding and are interested in the topics presented.
Complete beginners might have a hard time understanding some of the concepts portrayed in the article. 
I advise experienced coders to check out the [API Documentation](https://disease.sh/docs) and my [API Wrapper](https://github.com/novelcovid/node-api). 

# Preparation
We are going to build a Node.js project using Express and EJS (a templating engine). You will need to have Node.js installed.
- Create a new folder for your project, you can choose any name but I will call mine `covid-dashboard`.
- Open the folder in your terminal and run `npm init -y` to generate a package.json file.
- Create a file called `app.js` and two folders called `public` and `views` in the root of the project.
- In `public/`, create a `main.css` file and in `views/`, create a `index.ejs` file.

Your project directory should now look like this:
```
  covid-dashboard
  ├── package.json
  ├── app.js
  ├── public
  │   └── main.css
  └── views
      └── index.ejs
```

# Step 1: Installing dependencies
Alright, so after preparing our project, we can start adding our dependencies. For this project, we are going to need 3 packages:
- Express (web framework)
- EJS (templating engine)
- NovelCOVID (API wrapper)

Install them by running: <br>`npm install --save express ejs novelcovid`

# Step 2: Setting up the server
After installing all the packages we will use, it's time to code!
- First we need to require our packages `express` and `novelcovid`, then we can create our express object by calling `express()`.
- After that we set our view directory to the folder `views/` and specify our view engine, in this case `ejs`. Then we tell express to expose the contents of `public/` on the server root so we can use them in the frontend. 
- Now we can start our server by calling `app.listen(...)` and passing in our port of choice, in my case 8080, and a callback. 

I have also specified a GET route in the example below. If you start your server now using `node ./app.js` and open `localhost:8080/` in your browser, you will receive a greeting.
**The server works!**
```js 
// app.js
const express = require('express');
const api = require('novelcovid');

const app = express();

app.set('views', './views');
app.set('view engine', 'ejs');
app.use(express.static('./public'));

app.get('/*', (req, res) => res.send('Hello!'));

app.listen(8080, () => console.log('running on port 8080'));
```

# Step 3: Creating our views
In this step, we are going to use EJS to interpolate javascript variables into EJS templates to create HTML that can be sent to the client. It sounds way more complicated than it actually is, you will see in a bit.
In EJS you can use `<%= variable %>` to interpolate variables into your EJS template which will then be transformed into HTML. 
```html
<!-- index.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
  <title>COVID19 Dashboard</title>
  <link rel="stylesheet" href="/main.css">
</head>
<body>
  <div class="container">
    <h1>COVID19 Dashboard</h1>
    <table>
      <thead>
        <tr>
          <th>Name</th> 
          <th>Cases</th> 
          <th>Active</th> 
          <th>Recovered</th> 
          <th>Deaths</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Global</td>
          <td><%= cases %></td>
          <td><%= active %></td>
          <td><%= recovered %></td>
          <td><%= deaths %></td>
        </tr>
      </tbody>
    </table>
  </div>
</body>
</html>
```
*Don't worry if you don't understand what this means yet, just keep on reading.*

# Step 4: Displaying data using EJS
The EJS example above shows a table with 1 row, the first column of the row being 'Global' and its other columns being interpolated variables. 
That's all we need to do in EJS.
> But where do these values come from and how do we pass them over to the template? 

Let me break this up into 2 small steps, **getting the data** and **passing it to the template**.
- **Getting the data**
As mentioned at the beginning, we will use the `novelcovid` package to retrieve data from the API. We will use the function `api.all()` to fetch global data. Don't forget that the wrapper exposes asynchronous functions, so you have to make the request handler async.
- **Passing it to the template**
`res.render(...)` lets you specify a template view's name and optional variables to be interpolated and sends the HTML created from that template view and the variables passed to the client.
```js
// app.js
...
app.get('/*', async (req, res) => {
  const global = await api.all();
  res.render('index', { global });
});
...
```
If you start up your server again using `node ./app.js` and head over to `localhost:8080/` in your browser, you should see something like this:

![Dashboard with global data](https://user-images.githubusercontent.com/17516174/82122085-b622af80-9791-11ea-83ca-62b91f7f6601.png)

Amazin how easy that was, right? But it doesn't really look that good, yet. 
Let's add all countries to the list and then use CSS to style the table.

# Step 5: Country Data and Styling
The `novelcovid` package exposes a wide variety of functions to get data from the different endpoints we offer. One of them is `api.countries()`, which lets you retrieve a list of all countries and their data. **Exactly what we need!**

> But how can we make EJS create a table row for each country?

That's very easy. EJS lets you do loops and if clauses. I mean the name literally is 'Embedded JavaScript', just let that sink in. If you wanna know more, head over to their [website](https://ejs.co). 
```html
<!-- index.ejs -->
...
<tbody>
    <tr>
        <td>Global</td>
        <td><%= cases %></td>
        <td><%= active %></td>
        <td><%= recovered %></td>
        <td><%= deaths %></td>
    </tr>
    <% for (const country of countries) { %>
        <tr>
            <td><%= country.country %></td>
            <td><%= country.cases %></td>
            <td><%= country.active %></td>
            <td><%= country.recovered %></td>
            <td><%= country.deaths %></td>
        </tr>
    <% } %>
</tbody>
...
```
After updating our template, we now need to pass a variable called `countries` over to our EJS template in our request handler, so let's do that.
```js
// app.js
...
app.get('/*', async (req, res) => {
  const global = await api.all();
  const countries = await api.countries({ sort: 'cases' });
  res.render('index', { global, countries });
});
...
```

Spin up the server again and you should now see a big list of countries. Still ugly. 
Let's add some styling. You can find my example `main.css` below but you can style it however you like!

The `.container` class is our wrapper for the table, I will make it a maximum width of 700 pixels and center it. The stylings for `tr`, `th` and `td` elements is for the table.

```css
# main.css
body {
  box-sizing: border-box;
  margin: 0;
  font-family: 'Lucida Sans', Geneva, Verdana, sans-serif;
}

.container {
  width: 700px;
  text-align: center;
  margin: 0 auto;
  padding: 25px 0 0 0;
}

tr {
  text-align: left;
}

tbody > tr:not(:nth-child(2n)) {
  background-color: #eee;
}

th {
  font-size: 125%;
  padding-bottom: 10px;
}

td {
  padding: 5px;
}

td:not(:first-child) {
  text-align: right;
}
```

### The dashboard is finished! Look how easy that was!
![COVID-Dashboard](https://user-images.githubusercontent.com/17516174/82123776-6fd24e00-979b-11ea-9704-ec240159a11f.png)

You can find all the code on [Github](https://github.com/puf17640/covid-dashboard).

I really hope you liked the article, if not, please send me feedback to my [email](mailto:puf17640@spengergasse.at). If you have any questions regarding the NovelCOVID API, join our [Discord Server](https://discord.gg/cEDxzfW).

# Happy coding! - Julian