How to Create Beautiful HTML Outputs With Nodejs?

By Prajwal Haniya

Techletter #20 | April 29, 2023

How will you send an HTML page from the server as a response to a request? As a full-stack engineer, I usually write my code in React, but very rarely sent something from the backend. I have done this previously during my college project. But, recently I started to understand this better. The backend development is just beautiful. And most importantly you will have a lot to learn.

Project Structure

Project/
├── data/
│   └── data.json
├── files/
│   ├── main.html
│   └── template-blog.html
└── app.js

This is just a simple structure that I have made, don’t consider this as a final structure. But, will make use of this to send an HTML page with dynamic data.

files/main.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">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
    <title>Blog</title>
</head>
<body>
    <div class="p-4 row">
            <!-- <span class="m-2"> -->
                {%content%}
            <!-- </span> -->
    </div>
</body>
</html>

files/template-blog.html

<div class="card m-2" style="width: 18rem;">
    <div class="card-body">
      <h5 class="card-title">{%blogTitle%}</h5>
      <p class="card-text">{%blogDescription%}</p>
      <a href="blog/{%blogId%}/{%blogUrl%}" class="btn btn-primary">Read More</a>
    </div>
</div>

Now let us start the server, and create an API that responds with an HTML page.

Below is the explanation and is followed by the complete code.

Import the required module → load the templates outside the API → write an API that can send the HTML as a response with dynamic content.

So the API looks something like this

app.get('/', (req, res) => {
    const cards = posts.map((post) => replaceTemplate(cardTemplate, post)).join('');
    const finalPage = mainPage.replace(/{%content%}/g, cards);
    res.send(finalPage);
});

Why to load the html templates outside API? Because, you don’t want to load it everytime the api call is made. Assume you have 1 million api calls, you need to load it 1 million times. But, if you load outside, you only need to do it once.

We use the map to loop through each post (posts are imported from data.json) and pass the loaded HTML template to the function replaceTemplate. Which in turn returns the HTML string in an array which you need to join in order to just get a single string .

the replaceTemplate function is as follows

const replaceTemplate = (template, post) => {
    let output = template.replace(/{%blogTitle%}/g, post.title);
    output = output.replace(/{%blogDescription%}/g, post.content);
    output = output.replace(/{%blogId%}/g, post.id)
    output = output.replace(/{%blogUrl%}/g, post.slug);
    return output;
}

// when you console.log the cards in the API you get the following

console.log(cards);
// output
[
  '<div class="card m-2" style="width: 18rem;">\n' +
    '    <div class="card-body">\n' +
    '      <h5 class="card-title">A Blog Post About Cats</h5>\n' +
    '      <p class="card-text">This is a wonderful post about cats. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Porro dolore officiis atque voluptas, quas, repellendus cum, magnam a alias unde reiciendis voluptates aliquam maxime laborum? Quae omnis eius impedit et?</p>\n' +
    '      <a href="blog/1/the-cat-post" class="btn btn-primary">Read More</a>\n' +
    '    </div>\n' +
    '  </div>',
  '<div class="card m-2" style="width: 18rem;">\n' +
    '    <div class="card-body">\n' +
    '      <h5 class="card-title">The Dog Post</h5>\n' +
    '      <p class="card-text">This is an amazing post all about dogs. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Porro dolore officiis atque voluptas, quas, repellendus cum, magnam a alias unde reiciendis voluptates aliquam maxime laborum? Quae omnis eius impedit et?</p>\n' +
    '      <a href="blog/2/dogs-article" class="btn btn-primary">Read More</a>\n' +
    '    </div>\n' +
    '  </div>',
  '<div class="card m-2" style="width: 18rem;">\n' +
    '    <div class="card-body">\n' +
    '      <h5 class="card-title">Birds Are Great</h5>\n' +
    '      <p class="card-text">This is a once in a lifetime amazing post about birds. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Porro dolore officiis atque voluptas, quas, repellendus cum, magnam a alias unde reiciendis voluptates aliquam maxime laborum? Quae omnis eius impedit et?</p>\n' +
    '      <a href="blog/3/birds-are-great" class="btn btn-primary">Read More</a>\n' +
    '    </div>\n' +
    '  </div>'
]

You need to perform join('') in order to get a single string which is sent to the main.html replacing the {%content%}.

Final code

const app = require('express')();
const fs = require('fs');

const cardTemplate = fs.readFileSync(`${__dirname}/files/template-blog.html`, 'utf-8');
const mainPage = fs.readFileSync(`${__dirname}/files/main.html`, 'utf-8');
const posts = require('./data/data');

const replaceTemplate = (template, post) => {
    let output = template.replace(/{%blogTitle%}/g, post.title);
    output = output.replace(/{%blogDescription%}/g, post.content);
    output = output.replace(/{%blogId%}/g, post.id)
    output = output.replace(/{%blogUrl%}/g, post.slug);
    return output;
}

app.get('/', (req, res) => {
    const cards = posts.map((post) => replaceTemplate(cardTemplate, post)).join('');
    const finalPage = mainPage.replace(/{%content%}/g, cards);
    res.send(finalPage);
});

app.listen(3000, () => {
    console.log('Server is listening at port 3000');
});

Nowadays, the front end is not as easy as passing an HTML page. There involves a lot of logic. Doing it from the server side can be difficult & eat out a lot of developer’s time. So, using client-side libraries like React can make your development easier. But, occasionally you need the server to send the HTML responses. For, example sending emails, OTPs, etc. which are backend tasks need this concept to function efficiently.