DEV Community

Cover image for Day 4 - Your first week in NodeJS
Gurpreet Singh
Gurpreet Singh

Posted on • Originally published at gsin.in

Day 4 - 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);

Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode
// users.json

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

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>
Enter fullscreen mode Exit fullscreen mode
<!-- 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>
Enter fullscreen mode Exit fullscreen mode
<!-- 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>
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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"));
Enter fullscreen mode Exit fullscreen mode

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.

Useful Links

Discussion (0)