Day 4 - Your first week in NodeJS

Day 4 - Your first week in NodeJS

Template engines, Partials, Middlewares, Serving static assets

Gurpreet Singh

Published on Aug 8, 2021

6 min read

Subscribe to my newsletter and never miss my upcoming articles

Listen to this article

As promised, this is my fourth article of Your first week in NodeJS web article series.

Prerequisite

  1. Day 1 - Your first week in NodeJS
  2. Day 2 - Your first week in NodeJS
  3. Day 3 - Your first week in NodeJS

Quick recap

I believe by now you are already comfortable with core modules of Node.js and have created your own RESTFul API service.

It's time to create a front-end now, which you can use to show users information which we developed in the last article.

Template engines

Node.js has by default support for template engines, you can add these template engines as an npm package and start using them. It helps to send dynamic data on a template file which you can further process (server-side) and render as an HTML page.

Serve html file as response

// app.js

const app = require("express")();

app.get("/", (req, res) => {
  res.sendFile(`${__dirname}/index.html`);
});

app.get("/contact.html", (req, res) => {
  res.sendFile(`${__dirname}/contact.html`);
});

app.listen(3000);

We are serving .html files based on user requests. But you can't send anything dynamic in these files, and for that, you need a template engine.

To serve dynamic content, we will be using the ejs templating engine which is recommended because it is very fast.

You can install it by using $ npm install ejs. Now we have ejs installed, let's quickly make some changes to our app.js file to integrate it.

You have to do a couple of things here since the EJS template engine looks for the views folder, we will create the views folder under the root folder of our application.

// package.json

{
  "name": "node",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "ejs": "^3.1.6",
    "express": "^4.17.1",
    "nodemon": "^2.0.12"
  }
}
// users.json

[
  {
    "_id": "610ec10c78f7665bfa2cbf11",
    "age": 28,
    "name": "Bobbi Cannon",
    "email": "bobbicannon@dragbot.com"
  },
  {
    "_id": "610ec10ca916e321b1363301",
    "age": 33,
    "name": "Downs Burns",
    "email": "downsburns@dragbot.com"
  }
]

Partials

<!-- views/user.ejs -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Welcome | <%= data.name %></title>
  <style>
    body {
      background-color: cornflowerblue;
      text-align: center;
      font-size: 16px;
      font-family: Verdana, Geneva, Tahoma, sans-serif;
    }
    h1 {
      font-size: 3rem;
    }
    ul li {
      list-style: none;
      display: inline-block;
      margin: 0 10px;
    }
    a {
      font-size: 1.2rem;
      color: white;
      text-decoration: none;
    }
    a:hover {
      text-decoration: underline;
    }
    ul.users {
      margin: 0;
      padding: 0;
    }
    ul.users li {
      display: block;
      margin-bottom: 1rem;
    }
    .container {
      max-width: 600px;
      margin: 0 auto;
      text-align: left;
    }
  </style>
</head>
<body>
  <h1>Welcome | <%= data.name %> </h1>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
  <div class="container">
    <h2>User details</h2>
    <p><strong>Age: </strong><%= data.age || "N/A" %> </p>
    <p><strong>Email: </strong><%= data.email || "N/A" %> </p>
    <p><a href="/">&LessLess; Back</a></p>
  </div>
</body>
</html>
<!-- views/index.ejs -->

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Welcome</title>
  <style>
    body {
      background-color: cornflowerblue;
      text-align: center;
      font-size: 16px;
      font-family: Verdana, Geneva, Tahoma, sans-serif;
    }
    h1 {
      font-size: 3rem;
    }
    ul li {
      list-style: none;
      display: inline-block;
      margin: 0 10px;
    }
    a {
      font-size: 1.2rem;
      color: white;
      text-decoration: none;
    }
    a:hover {
      text-decoration: underline;
    }
    ul.users {
      margin: 0;
      padding: 0;
    }
    ul.users li {
      display: block;
      margin-bottom: 1rem;
    }
    .container {
      max-width: 600px;
      margin: 0 auto;
      text-align: left;
    }
  </style>
</head>
<body>
  <h1>Welcome to my webpage</h1>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
  <div class="container">
    <h2>Active Users</h2>
    <ul class="users">
      <% data.forEach(function(user){ %>
        <li><a href="/user/<%= user._id %>"><%= user.name %> (<%= user.age %>)</a></li>
      <% }); %>
    </ul>
  </div>
</body>
</html>
<!-- contact.html -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact</title>
  <style>
    body {
      background-color: cornflowerblue;
      text-align: center;
      font-size: 16px;
      font-family: Verdana, Geneva, Tahoma, sans-serif;
    }
    h1 {
      font-size: 3rem;
    }
    ul li {
      list-style: none;
      display: inline-block;
      margin: 0 10px;
    }
    a {
      font-size: 1.2rem;
      color: white;
      text-decoration: none;
    }
    a:hover {
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <h1>Contact me on Twitter @gsin11</h1>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
  <p>This page is running on awesome Node.js sever!</p>
</body>
</html>

Server file app.js

// app.js

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
let USER_DATA = require("./users.json");

app.set("view engine", "ejs");  // setting up template engine to ejs

app.use((req, res, next) => {
  res.append("Access-Control-Allow-Origin", ["*"]);
  res.append("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
  res.append("Access-Control-Allow-Headers", "Content-Type");
  if (req.url.includes("/api")) {
    res.append("Content-Type", "text/json");
  }
  next();
});

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

function makeId(length) {
  var result = "";
  var characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  var charactersLength = characters.length;
  for (var i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}

const getUserInfo = (id) => {
  return USER_DATA.filter((obj) => obj._id === id);
};

app.get("/", (req, res) => {
  res.render("index", {
    data: USER_DATA,
  });
});

app.get("/user/:id", (req, res) => {
  res.render("user", {
    data: getUserInfo(req.params.id)[0] || {},
  });
});

app.get("/contact", (req, res) => {
  res.sendFile(`${__dirname}/contact.html`);
});

// RESTFul API Services

// get all users
app.get("/api/users", (req, res) => {
  res.send(USER_DATA);
});

// get single user by id
app.get("/api/user/:id", (req, res) => {
  res.send(getUserInfo(req.params.id));
});

// delete single user by id
app.delete("/api/user/:id", (req, res) => {
  const index = USER_DATA.findIndex((obj) => obj._id === req.params.id);
  res.send(USER_DATA.splice(index, 1));
});

// add new user
app.post("/api/user", (req, res) => {
  const { name, age, email } = req.body;
  if (name && age && email) {
    USER_DATA = [
      ...USER_DATA,
      {
        _id: makeId(24),
        age,
        name,
        email,
      },
    ];
    res.send(USER_DATA);
  } else {
    res.status(500).send("error, user not added.");
  }
});

app.listen(3000);

Snapshots

Homepage

homepage

Contact page

contact-page

User details

user-details

Middleware and static assets

You have seen in the above templates we are using CSS as inline, in this segment we will include CSS files as static assets as well as images.

First, we need to move inline CSS styles to an external stylesheet.

/* assets/styles.css */

body {
  background-color: cornflowerblue;
  text-align: center;
  font-size: 16px;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
}
h1 {
  font-size: 3rem;
}
ul li {
  list-style: none;
  display: inline-block;
  margin: 0 10px;
}
a {
  font-size: 1.2rem;
  color: white;
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
ul.users {
  margin: 0;
  padding: 0;
}
ul.users li {
  display: block;
  margin-bottom: 1rem;
}
.container {
  max-width: 600px;
  margin: 0 auto;
  text-align: left;
}

Now we can include styles.css file with <link href="/assets/styles.css" rel="stylesheet" type="text/css" /> in all template and HTML files.

Now we need to inform our server that we are going to load the static files. By using the app.use function we can call express.static middleware to include static files.

// add this in your app.js

app.use("/assets", express.static("assets"));

Now you can restart your server and will see another request in networks for the CSS file. Similarly, you can include images/videos, etc.

You can create more routes with add/delete user functionality as follow:

  • Create a new route /user/add followed by a form to add new users.

  • Provide a button to delete an existing user by invoking API user/:id with the DELETE method.

 
Share this