Full Stack CRUD Application in Node JS using ajax

Recently I have written an article on CRUD operation using node js and you guys loved it. But frankly that was only for learning purpose, because in real application you need to implement Ajax functionally to achieve faster , secured and most important internal frontend and backend interaction without page refresh.

Now as you guys know I like to create everything from scratch , so in this article you will be leaning a complete full stack application using NODE JS , EXPRESS JS , JAVA SCRIPT and AJAX.

Before continuing , I know you guys might be having doubt like if we can built an application without ajax , then why we need ajax and why is it so important.

What is AJAX and why is it important ?

You will come to know the power of ajax as we go along this article, but let keep it precise here.

AJAX stands for Asynchronous JavaScript and XML.

As per w3school , “AJAX allows web pages to be updated asynchronously by exchanging small amounts of data with the server behind the scenes. This means that it is possible to update parts of a web page, without reloading the whole page.

Key point to know about AJAX –

  • Ajax is a technique and not a language.
  • Used to send data to server and receive response behind the scene without blocking the execution of other parts of the JavaScript program.
  • Use to update part of dom without refreshing whole page
  • Efficient way for a web application to handle user interactions with a web page

Now that we understood the basics of ajax , lets start coding .

Step 1 – Initiate a node js server

  • Initiate a node js application
  • Install all the required dependencies
npm init
npm install mysql express nodemon dotenv cors body-parser ejs

Step 2 – Create your first API

Before creating out first api, add this line in your package.json file.

nodemon basically helps in automatically restarting the node application when file changes in the directory are detected

Now lets go to server.js file (create on if not created automatically) and write some code.

server.js

const express = require("express"); //express package initiated
const app = express(); // express instance has been created and will be access by app variable

app.get("/", (req, res) => {
  res.send("API running");
});

app.listen(3000);

Go to terminal and type : npm start

Go to broweser and type : http://localhost:3000/

Boom! You have successfully created your first API.

Step 3 – Let’s create database

For this application we will be using MySQL database .

  • Install Xampp (to create and run server in local system )
  • Start MySQL and apache server
  • Go to “http://localhost/phpmyadmin/”
Create Database , Create Table , Create Column i.e id , name , email , phone.

Click on go , and Boom! Database Created.

USERNAME and PASSWORD are created by default. You can go to privileged section of myPhpAdmin and change the password accordingly.

Step 4 – Connect database with backend

Before connecting , create .env file

-> .env file helps in keeping all the connection details separate 
->  Also while pushing the code , we can ignore .env file and make our code secure.

.env file :

Create config/db.js file

const dotenv = require("dotenv");
const mysql = require("mysql");
dotenv.config();

const connection = mysql.createConnection({
  host: process.env.HOST,
  user: process.env.USER,
  password: process.env.PASSWORD,
  database: process.env.DATABASE,
});

connection.connect((error) => {
  if (error) return console.log(error);
  console.log("connection successfull");
});

module.exports = connection;

Update server.js file

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

const connection = require("./config/db");
dotenv.config();

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

app.get("/", (req, res) => {
  res.send("hello world 2");
});

app.listen(process.env.PORT || 3000, function (err) {
  if (err) console.log(err);
  console.log(`listening to port ${process.env.PORT}`);
});

Step 5 – Setting up frontend

Now to make your life and my life easy, I will be using bootstrap 5 for most of css stuffs , since our main focus is to learn the end to end crud operation.

Boostrap css cdn

//bootstrap css CDN
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

jQuery cdn

//jQuery 3.6 CDN
<script src="https://code.jquery.com/jquery-3.6.1.js" integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=" crossorigin="anonymous"></script>

public/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" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="container my-2">
      <h1 class="h1 text-center">Fill the data</h1>
      <form id="myForm">
        <div class="mb-3">
          <label class="form-label">Name</label>
          <input type="text" class="form-control" name="name" id="formName" />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input type="email" class="form-control" name="email" id="formEmail"/>
        </div>
        <div class="mb-3">
          <label class="form-label">Phone</label>
          <input type="number" class="form-control" name="number" id="formNumber"/>
        </div>

        <button type="submit" class="btn btn-primary enableOnInput">Submit</button>
      </form>
    </div>

    <script
      src="https://code.jquery.com/jquery-3.6.1.js"
      integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
      crossorigin="anonymous"
    ></script>
  </body>
</html>

public/style.css

*{
    margin:0;
    padding:0;
    box-sizing: border-box;
    color: white;
}

body {
    background: rgb(2,0,36);
    background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,63,121,0.7719420531884629) 75%, rgba(120,194,209,1) 100%); 
}

.errormsg , .errormsg1 , .errormsg2{
    color: red;
    font-style: italic;
}

Step 6 – Form Validation (Frontend)

Here we need to validate from frontend side that user is entering the data as per rules or not. Below are the code for all the 3 field validation.

Note – Many inbuilt packages are their for form validation , but I preferred to do it manually for your better understanding.

 <script>
      //name field validation
      function nameValidation() {
        var nameRegex = /^[A-Za-z ]+$/;
        var nameInput = $("#formName").val();
        $(".errormsg1").remove();
        if (nameInput.length < 10 && nameRegex.test(nameInput)) {
          return 1;
        } else {
          $("#formName").after(`<div class="errormsg1">Invalid Name!</div>`);
          return 0;
        }
      }

      // number input validation
      function numberValidation() {
        console.log("number validation");
        $(".errormsg2").remove();
        var a = $("#formNumber").val();
        var filter = /^[0-9]*$/;
        if (filter.test(a)) {
          console.log("-------", filter.test(a));
          if (a.length != 10) {
            $("#formNumber").after(
              `<div class="errormsg2">Please enter 10 digit number</div>`
            );
            return 0;
          } else {
            $(".errormsg2").remove();
            return 1;
          }
        } else {
          $("#formNumber").after(
            `<div class="errormsg2">Please enter correct phone number</div>`
          );
          return 0;
        }
      }

      // email input validation
      function emailValidation() {
        $(".errormsg").remove();
        var emailData = $("#formEmail").val();
        var testEmail = /^[A-Z0-9._%+-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i;
        if (testEmail.test(emailData)) {
          $(".errormsg").remove();
          return 1;
        } else {
          $("#formEmail").after(
            `<div class="errormsg">Email cant be empty!</div>`
          );
          return 0;
        }
      }

      //disabling button
      function buttonFade() {
        $(".enableOnInput").prop("disabled", true);
        if (
          nameValidation() == 1 &&
          emailValidation() == 1 &&
          numberValidation() == 1
        ) {
          $(".enableOnInput").prop("disabled", false);
        }
      }

      $(document).ready(function () {
        $(".enableOnInput").prop("disabled", true);
        $("#formName").keyup(function () {
          nameValidation();
          buttonFade();
        });
        $("#formEmail").keyup(function () {
          emailValidation();
          buttonFade();
        });
        $("#formNumber").keyup(function () {
          numberValidation();
          buttonFade();
        });
      });
    </script>
Few regex - 
----------
nameRegex = /^[A-Za-z ]+$/;
emailRegex =  /^[A-Z0-9._%+-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i;
phoneRegex =/^[0-9]*$/;

Step 7 – Create Operation using Ajax

Earlier when we use to perform create operation without ajx, we use to pass the url inside action attribute of form tag , and everytime the page needs to be sumbitted/refresh to send the data to backend.

Also, we cant perform any other action while submit event is going on.

But not anymore , with the help of ajax –

  • Submit the form asynchronously
  • No page refresh required while sending the data to backend.
  • Multiple logic can be applied while submitting , as per requirement.

ajax code snippet for create operation –

$("#myForm").submit(function (e) {
          e.preventDefault();
          var ajaxData = $(this).serialize();
          console.log("data on click..", ajaxData);

          $.ajax({
            type: "POST",
            url: "/create",
            data: ajaxData,
            // data: {
            //   name: $("#formName").val(),
            //   email: $("#formEmail").val(),
            // },
            // async : flase,
            // dataType: "json",
            // encode: true,
            success: function (response, status) {
              console.log(status, response);
              if (status == "success") {
                $("#message").html(
                  `<div class="alert alert-success" role="alert">Data inserted succesfully.</div>`
                );

                setTimeout(() => {
                  window.location.replace(response.url);
                }, 2000);
              } else {
                $("#message").html(
                  `<div class="alert alert-danger" role="alert">Something went wrong.</div>`
                );
              }
            },
            error: function (err, status) {
              console.log("status - ", status);
              console.log(err, status);
              $("#message").html(
                `<div class="alert alert-danger" role="alert">Something went wrong.</div>`
              );
            },
          });
        });

Updated 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" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="container my-2">
      <div id="message" class="m-2"></div>
      <h1 class="h1 text-center">Fill the data</h1>
      <form id="myForm">
        <div class="mb-3">
          <label class="form-label">Name</label>
          <input type="text" class="form-control" name="name" id="formName" />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input
            type="email"
            class="form-control"
            name="email"
            id="formEmail"
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Phone</label>
          <input
            type="text"
            class="form-control"
            name="number"
            id="formNumber"
          />
        </div>

        <button type="submit" class="btn btn-danger enableOnInput">
          Submit
        </button>
      </form>
    </div>

    <script
      src="https://code.jquery.com/jquery-3.6.1.js"
      integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
      crossorigin="anonymous"
    ></script>

    <script>
      // name input validation
      function nameValidation() {
        $(".errormsg").remove();
        if ($("#formName").val() == "") {
          $("#formName").after(
            `<div class="errormsg">Name cant be empty!</div>`
          );
          return 0;
        }
        $(".errormsg").remove();
        return 1;
      }

      // email input validation
      function emailValidation() {
        $(".errormsg").remove();
        var emailData = $("#formEmail").val();
        var testEmail = /^[A-Z0-9._%+-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i;
        if (testEmail.test(emailData)) {
          $(".errormsg").remove();
          return 1;
        } else {
          $("#formEmail").after(
            `<div class="errormsg">Email cant be empty!</div>`
          );
          return 0;
        }
      }

      // number input validation
      function numberValidation() {
        $(".errormsg").remove();
        var a = $("#formNumber").val();
        var filter = /^[0-9-+]+$/;
        if (filter.test(a)) {
          if (a.length != 10) {
            $("#formNumber").after(
              `<div class="errormsg">Please enter 10 digit number</div>`
            );
            return 0;
          }
          $(".errormsg").remove();
          return 1;
        } else {
          $("#formNumber").after(
            `<div class="errormsg">Please enter correct phone number</div>`
          );
          return 0;
        }
      }

      function buttonFade() {
        $(".enableOnInput").prop("disabled", true);
        if (
          nameValidation() == 1 &&
          emailValidation() == 1 &&
          numberValidation() == 1
        ) {
          $(".enableOnInput").prop("disabled", false);
        }
      }

      $(document).ready(function () {
        buttonFade();

        $("#formName").keyup(function () {
          nameValidation();
          buttonFade();
        });

        $("#formEmail").keyup(function () {
          emailValidation();
          buttonFade();
        });

        $("#formNumber").keyup(function () {
          numberValidation();
          buttonFade();
        });

        $("#myForm").submit(function (e) {
          e.preventDefault();
          var ajaxData = $(this).serialize();
          console.log("data on click..", ajaxData);

          $.ajax({
            type: "POST",
            url: "/create",
            data: ajaxData,
            // data: {
            //   name: $("#formName").val(),
            //   email: $("#formEmail").val(),
            // },
            // async : flase,
            // dataType: "json",
            // encode: true,
            success: function (response, status) {
              console.log(status, response);
              if (status == "success") {
                $("#message").html(
                  `<div class="alert alert-success" role="alert">Data inserted succesfully.</div>`
                );

                setTimeout(() => {
                  window.location.replace(response.url);
                }, 2000);
              } else {
                $("#message").html(
                  `<div class="alert alert-danger" role="alert">Something went wrong.</div>`
                );
              }
            },
            error: function (err, status) {
              console.log("status - ", status);
              console.log(err, status);
              $("#message").html(
                `<div class="alert alert-danger" role="alert">Something went wrong.</div>`
              );
            },
          });
        });
      });
    </script>
  </body>
</html>

Updated server.js file

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

const connection = require("./config/db");
dotenv.config();

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.set("view engine", "ejs");
app.use(express.json());
app.use(express.static(__dirname + "/public"));
app.use(express.static(__dirname + "/views"));

app.get("/", (req, res) => {
  res.redirect("/index.html");
});

app.get("/read", (req, res) => {
  res.render("read.ejs");
});

//create operation
app.post("/create", (req, res) => {
  var name = req.body.name;
  var email = req.body.email;
  var phone = req.body.number;

  console.log(name + " " +email + " " + phone);

  connection.query(
    "INSERT into student_details (name,email,phone) values(?,?,?)",
    [name, email, phone],
    function (err, result) {
      console.log("data.... ", result);
      if (err) {
        res.status(500).json({ message: err });
      } else {
        res.status(200).send({ message: "submitted", url: "/read" });
      }
    }
  );
});

app.listen(process.env.PORT || 3000, function (err) {
  if (err) console.log(err);
  console.log(`listening to port ${process.env.PORT}`);
});

Step 8 – Read Operation using Ajax

Now once user have filled the form , they should be redirected to a page where they can see all the form data . This is nothing but read operation.

(server.js) Creating router for read operation –

//read operation
app.get("/read", (req, res) => {
  connection.query("select * from student_details", function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      res.render("read.ejs", { rows });
    }
  });
});

Now we need to create one more html file , where we can display all the data.

But since we are not using any framework , we can’t dynamically render html pages. To achieve this , we need some template engine to dynamically render HTML page . In this article, we will be use “ejs” templating engine. Read more

views/read.ejs (create an ejs file inside views folder)

<!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.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" type="text/css" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="container">
      <div class="d-flex flex-row justify-content-between bd-highlight my-3">
        <h1 class="h3 text-center">All data</h1>

        <a href="/">
          <button class="btn btn-success">Create</button>
        </a>
      </div>
      <div class="row">
        <% if(rows.length > 0) { %> <% rows.map((eachData) => { %>

        <div class="col-lg-3 col-md-4 col-sm-6 col-xs-12 my-1">
          <div class="card">
            <div class="card-body">
              <h5 class="card-title text-dark"><%- eachData.name %></h5>
              <h6 class="card-subtitle mb-2 text-muted">
                <%- eachData.email %>
              </h6>
              <h6 class="card-subtitle mb-2 text-muted">
                <%- eachData.phone %>
              </h6>
              <a href="#" class="card-link text-success">Edit</a>
              <a href="#" class="card-link text-danger">Delete</a>
            </div>
          </div>
        </div>

        <% }) %> <%} else { %>
        <div class="col-md-12">
          <h1 class="h2 text-center">No data found</h1>
        </div>

        <% }%>
      </div>
    </div>
  </body>
</html>

Step 9 – Adding count at the top

Below image show the logic of adding count or the number of data present

Updated read.ejs file

<!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.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" type="text/css" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="container">
      <div class="d-flex flex-row justify-content-between bd-highlight my-3">
        <h1 class="h3 text-center">
          All data [
          <span id="row_count"><span><%- rows.length %> </span></span>]
        </h1>

        <a href="/">
          <button class="btn btn-success">Create</button>
        </a>
      </div>
      <div class="row">
        <% if(rows.length > 0) { %> <% rows.map((eachData) => { %>

        <div class="col-lg-3 col-md-4 col-sm-6 col-xs-12 my-1">
          <div class="card">
            <div class="card-body">
              <h5 class="card-title text-dark"><%- eachData.name %></h5>
              <h6 class="card-subtitle mb-2 text-muted">
                <%- eachData.email %>
              </h6>
              <h6 class="card-subtitle mb-2 text-muted">
                <%- eachData.phone %>
              </h6>
              <a href="#" class="card-link text-success">Edit</a>
              <a href="#" class="card-link text-danger">Delete</a>
            </div>
          </div>
        </div>

        <% }) %> <%} else { %>
        <div class="col-md-12">
          <h1 class="h2 text-center">No data found</h1>
        </div>

        <% }%>
      </div>
    </div>
  </body>
</html>

Step 10 – Delete Operation using Ajax with count update

Now here is the best part of using ajax . We can delete the data –

  • Without refreshing the page
  • The count number at the top will also be updated, and the data is coming from the backend.

Delete ajax logic with count update

<script>
      $(document).ready(function () {
        $(".delete_student").on("click", function (e) {
          e.preventDefault();
          var clicked_id = $(this).attr("id");
          var del_info = "id=" + clicked_id;
          console.log(del_info);
          $.ajax({
            type: "DELETE",
            url: "/delete-student",
            data: del_info,
            success: function (response, status) {
              console.log("delete response...", response);
              if (response.status == 200) {
               $("#card_" + clicked_id).fadeOut("slow");
                $("#row_count").html(`<span> ${response.row} </span>`);
                $("#message").html(
                  `<div class="alert alert-danger" role="alert">Data deleted succesfully.</div>`
                );
                $(document).scrollTop(0);
                setTimeout(() => {
                  $("#message").html(``);
                }, 1500);
              }
            },
            error: function (err, status) {
              console.log(err);
            },
          });
        });
      });
    </script>

Update server.js :

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

const connection = require("./config/db");
dotenv.config();

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.set("view engine", "ejs");
app.use(express.json());
app.use(express.static(__dirname + "/public"));
app.use(express.static(__dirname + "/views"));

app.get("/", (req, res) => {
  res.redirect("/index.html");
});

//read operation
app.get("/read", (req, res) => {
  connection.query("select * from student_details", function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      res.render("read.ejs", { rows });
    }
  });
});

//create operation
app.post("/create", (req, res) => {
  var name = req.body.name;
  var email = req.body.email;
  var phone = req.body.number;

  console.log(email + " " + phone);

  connection.query(
    "INSERT into student_details (name,email,phone) values(?,?,?)",
    [name, email, phone],
    function (err, result) {
      console.log("data.... ", result);
      if (err) {
        res.status(500).json({ message: err });
      } else {
        res.status(200).send({ message: "submitted", url: "/read" });
      }
    }
  );
});

//delete operation
app.delete("/delete-student", function (req, res) {
  console.log("deleted", req.body.id);
  const deleteQuery = "delete from student_details where id = ?";
  connection.query(deleteQuery, [req.body.id], function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      connection.query(
        "select count(*) as cnt from student_details",
        (err, count) => {
          if (err) {
            res.send(err);
          } else {
            console.log("count..", count[0].cnt);
            res.send({
              status: "200",
              message: "Row deleted",
              row: count[0].cnt,
            });
          }
        }
      );
    }
  });
});

app.listen(process.env.PORT || 3000, function (err) {
  if (err) console.log(err);
  console.log(`listening to port ${process.env.PORT}`);
});

updated read.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" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" type="text/css" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="container">
      <div class="d-flex flex-row justify-content-between bd-highlight my-3">
        <h1 class="h3 text-center">
          All data [
          <span id="row_count"><span><%- rows.length %> </span></span>]
        </h1>

        <a href="/">
          <button class="btn btn-success">Create</button>
        </a>
      </div>
      <div class="row">
        <% if(rows.length > 0) { %> <% rows.map((eachData) => { %>

        <div
          class="col-lg-3 col-md-4 col-sm-6 col-xs-12 my-1"
          id="card_<%-eachData.id %>"
        >
          <div class="card">
            <div class="card-body">
              <h5 class="card-title text-dark"><%- eachData.name %></h5>
              <h6 class="card-subtitle mb-2 text-muted">
                <%- eachData.email %>
              </h6>
              <h6 class="card-subtitle mb-2 text-muted">
                <%- eachData.phone %>
              </h6>
              <a href="#" class="card-link text-success">Edit</a>
              <a
                href="/delete-student?id=<%-eachData.id %>"
                class="card-link text-danger delete_student"
                id="<%-eachData.id %>"
                >Delete</a
              >
            </div>
          </div>
        </div>

        <% }) %> <%} else { %>
        <div class="col-md-12">
          <h1 class="h2 text-center">No data found</h1>
        </div>

        <% }%>
      </div>
    </div>

    <script
      src="https://code.jquery.com/jquery-3.6.1.js"
      integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
      crossorigin="anonymous"
    ></script>

    <script>
      $(document).ready(function () {
        $(".delete_student").on("click", function (e) {
          e.preventDefault();
          var clicked_id = $(this).attr("id");
          var del_info = "id=" + clicked_id;
          console.log(del_info);
          $.ajax({
            type: "DELETE",
            url: "/delete-student",
            data: del_info,
            success: function (response, status) {
              console.log("delete response...", response);
              if (response.status == 200) {
                $("#card_" + clicked_id).fadeOut("slow");
                $("#row_count").html(`<span> ${response.row} </span>`);
              }
            },
            error: function (err, status) {
              console.log(err);
            },
          });
        });
      });
    </script>
  </body>
</html>

Step 11 – Update Operation using Ajax

Update is one of the challenging operation and people often get confused in it , but here I have treid my best to explain you as easy as I can.

> Add link to update button of each card

<a
                href="/update-student?id=<%-eachData.id %>"
                class="card-link text-success update_student"
                id="<%-eachData.id %>"
                >Edit</a
              >

> Create update.ejs file

<!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.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Update</title>
  </head>
  <body>
    <div class="container my-2">
      <div id="message" class="m-2"></div>
      <h1 class="h1 text-center">Update Data</h1>
      <form id="myFormUpdate">
        <div class="mb-3">
          <label class="form-label">Name</label>
          <input
            type="text"
            class="form-control"
            name="name"
            id="formName"
            value="<%-data.name %>"
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input
            type="email"
            class="form-control"
            name="email"
            id="formEmail"
            value="<%-data.email %>"
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Phone</label>
          <input
            type="text"
            class="form-control"
            name="number"
            id="formNumber"
            value="<%-data.phone %>"
          />
        </div>

        <button type="submit" class="btn btn-info">Update</button>
        <button onclick="history.go(-1)" class="btn btn-dark">Back</button>
      </form>
    </div>

    <script
      src="https://code.jquery.com/jquery-3.6.1.js"
      integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
      crossorigin="anonymous"
    ></script>
  </body>
</html>

> Logic to pass user data to the file once they click on update

//passing data to update page

app.get("/update-student", (req, res) => {
  const updateQuery = "select * from  student_details where id=?";
  connection.query(updateQuery, [req.query.id], (err, eachRow) => {
    if (err) {
      res.send(err);
    } else {
      console.log("each data for edit", eachRow[0]);
      res.render("update.ejs", { data: eachRow[0] });
    }
  });
});

> Adding ajax update logic to “update” button

<script>
      $(document).ready(function () {
        $("#myFormUpdate").submit(function (e) {
          e.preventDefault();
          const updateData = $(this).serialize();
          console.log("update...", updateData);
          $.ajax({
            type: "POST",
            url: "/update",
            data: {
              hidden_id: $("#formId").val(),
              name: $("#formName").val(),
              email: $("#formEmail").val(),
              phone: $("#formNumber").val(),
            },
            success: function (response, status) {
              console.log("update response..", response);
              if (response.status == 200) {
                $("#update_message").html(
                  `<div class="alert alert-success" role="alert">Data updated succesfully.</div>`
                );
                setTimeout(() => {
                  window.location.replace(response.url);
                }, 1000);
              }
            },
            error: function (error, status) {
              console.log(error);
            },
          });
        });
      });
    </script>

Final update.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" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Update</title>
  </head>
  <body>
    <div class="container my-2">
      <div id="update_message" class="m-2"></div>
      <h1 class="h1 text-center">Update Data</h1>
      <form id="myFormUpdate">
        <div class="mb-3">
          <input type="hidden" name="id" id="formId" value="<%-data.id %>" />
          <label class="form-label">Name</label>
          <input
            type="text"
            class="form-control"
            name="name"
            id="formName"
            value="<%-data.name %>"
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input
            type="email"
            class="form-control"
            name="email"
            id="formEmail"
            value="<%-data.email %>"
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Phone</label>
          <input
            type="text"
            class="form-control"
            name="number"
            id="formNumber"
            value="<%-data.phone %>"
          />
        </div>

        <button type="submit" class="btn btn-info">Update</button>
        <button onclick="history.go(-1)" class="btn btn-dark">Back</button>
      </form>
    </div>

    <script
      src="https://code.jquery.com/jquery-3.6.1.js"
      integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
      crossorigin="anonymous"
    ></script>

    <script>
      $(document).ready(function () {
        $("#myFormUpdate").submit(function (e) {
          e.preventDefault();
          const updateData = $(this).serialize();
          console.log("update...", updateData);
          $.ajax({
            type: "POST",
            url: "/update",
            data: {
              hidden_id: $("#formId").val(),
              name: $("#formName").val(),
              email: $("#formEmail").val(),
              phone: $("#formNumber").val(),
            },
            success: function (response, status) {
              console.log("update response..", response);
              if (response.status == 200) {
                $("#update_message").html(
                  `<div class="alert alert-success" role="alert">Data updated succesfully.</div>`
                );
                setTimeout(() => {
                  window.location.replace(response.url);
                }, 1000);
              }
            },
            error: function (error, status) {
              console.log(error);
            },
          });
        });
      });
    </script>
  </body>
</html>

Final server.js :

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

const connection = require("./config/db");
dotenv.config();

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.set("view engine", "ejs");
app.use(express.json());
app.use(express.static(__dirname + "/public"));
app.use(express.static(__dirname + "/views"));

app.get("/", (req, res) => {
  res.redirect("/index.html");
});

//read operation
app.get("/read", (req, res) => {
  connection.query("select * from student_details", function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      res.render("read.ejs", { rows });
    }
  });
});

//create operation
app.post("/create", (req, res) => {
  var name = req.body.name;
  var email = req.body.email;
  var phone = req.body.number;

  console.log(email + " " + phone);

  connection.query(
    "INSERT into student_details (name,email,phone) values(?,?,?)",
    [name, email, phone],
    function (err, result) {
      console.log("data.... ", result);
      if (err) {
        res.status(500).json({ message: err });
      } else {
        res.status(200).send({ message: "submitted", url: "/read" });
      }
    }
  );
});

//delete operation
app.delete("/delete-student", function (req, res) {
  console.log("deleted", req.body.id);
  const deleteQuery = "delete from student_details where id = ?";
  connection.query(deleteQuery, [req.body.id], function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      connection.query(
        "select count(*) as cnt from student_details",
        (err, count) => {
          if (err) {
            res.send(err);
          } else {
            console.log("count..", count[0].cnt);
            res.send({
              status: "200",
              message: "Row deleted",
              row: count[0].cnt,
            });
          }
        }
      );
    }
  });
});

//passing data to update page
app.get("/update-student", (req, res) => {
  const updateQuery = "select * from  student_details where id=?";
  connection.query(updateQuery, [req.query.id], (err, eachRow) => {
    if (err) {
      res.send(err);
    } else {
      res.render("update.ejs", { status: 200, data: eachRow[0] });
    }
  });
});

//final update
app.post("/update", (req, res) => {
  const id_data = req.body.hidden_id;
  const name_data = req.body.name;
  const email_data = req.body.email;
  const phone_data = req.body.phone;
  console.log("data to be updated..", name_data);

  const updateQuery =
    "update student_details set name=?, email=? , phone=? where id=?";

  connection.query(
    updateQuery,
    [name_data, email_data, phone_data, id_data],
    (err, rows) => {
      if (err) {
        res.send(err);
      } else {
        return res.status(200).send({ status: "200", url: "/read" });
      }
    }
  );
});

app.listen(process.env.PORT || 3000, function (err) {
  if (err) console.log(err);
  console.log(`listening to port ${process.env.PORT}`);
});

Step 12 – Checking if detail already present in database

ajax logic applied don create end point –

//create operation
app.post("/create", (req, res) => {
  var name = req.body.name;
  var email = req.body.email;
  var phone = req.body.number;

  connection.query(
    "SELECT COUNT(*) AS CNT FROM student_details where email = ? or phone = ?",
    [email, phone],
    (err, data) => {
      if (err) {
        res.send(err);
      } else {
        console.log("count...", data[0].CNT);
        if (data[0].CNT > 0) {
          res.send({ status: 500, message: "Email/Phone already exist" });
        } else {
          connection.query(
            "INSERT into student_details (name,email,phone) values(?,?,?)",
            [name, email, phone],
            function (err, result) {
              if (err) {
                res.status(500).json({ status: 500, message: err });
              } else {
                res.status(200).send({
                  status: 200,
                  message: "Data Inserted Successfully",
                  url: "/read",
                });
              }
            }
          );
        }
      }
    }
  );
});

Final Updated server.js –

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

const connection = require("./config/db");
dotenv.config();

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.set("view engine", "ejs");
app.use(express.json());
app.use(express.static(__dirname + "/public"));
app.use(express.static(__dirname + "/views"));

app.get("/", (req, res) => {
  res.redirect("/index.html");
});

//read operation
app.get("/read", (req, res) => {
  connection.query("select * from student_details", function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      res.render("read.ejs", { rows });
    }
  });
});

//create operation
app.post("/create", (req, res) => {
  var name = req.body.name;
  var email = req.body.email;
  var phone = req.body.number;

  connection.query(
    "SELECT COUNT(*) AS CNT FROM student_details where email = ? or phone = ?",
    [email, phone],
    (err, data) => {
      if (err) {
        res.send(err);
      } else {
        console.log("count...", data[0].CNT);
        if (data[0].CNT > 0) {
          res.send({ status: 500, message: "Email/Phone already exist" });
        } else {
          connection.query(
            "INSERT into student_details (name,email,phone) values(?,?,?)",
            [name, email, phone],
            function (err, result) {
              if (err) {
                res.status(500).json({ status: 500, message: err });
              } else {
                res.status(200).send({
                  status: 200,
                  message: "Data Inserted Successfully",
                  url: "/read",
                });
              }
            }
          );
        }
      }
    }
  );
});

//delete operation
app.delete("/delete-student", function (req, res) {
  const deleteQuery = "delete from student_details where id = ?";
  connection.query(deleteQuery, [req.body.id], function (err, rows) {
    if (err) {
      res.send(err);
    } else {
      connection.query(
        "select count(*) as cnt from student_details",
        (err, count) => {
          if (err) {
            res.send(err);
          } else {
            console.log("count..", count[0].cnt);
            res.send({
              status: "200",
              message: "Row deleted",
              row: count[0].cnt,
            });
          }
        }
      );
    }
  });
});

//passing data to update page
app.get("/update-student", (req, res) => {
  const updateQuery = "select * from  student_details where id=?";
  connection.query(updateQuery, [req.query.id], (err, eachRow) => {
    if (err) {
      res.send(err);
    } else {
      res.render("update.ejs", { status: 200, data: eachRow[0] });
    }
  });
});

//final update
app.post("/update", (req, res) => {
  const id_data = req.body.hidden_id;
  const name_data = req.body.name;
  const email_data = req.body.email;
  const phone_data = req.body.phone;
  console.log("data to be updated..", name_data, phone_data, id_data);

  connection.query(
    "select count(*) as cnt from student_details where email = ? or phone = ?",
    [email_data, phone_data],
    (err, data) => {
      if (err) {
        res.send(err);
      } else {
        connection.query(
          "update student_details set name=?, email=?, phone=? where id=?",
          [name_data, email_data, phone_data, id_data],
          (err, rows) => {
            if (err) {
              res.send(err);
            } else {
              return res.send({
                status: "200",
                message: "Data Updated!",
                url: "/read",
              });
            }
          }
        );
      }
    }
  );
});

app.listen(process.env.PORT || 3000, function (err) {
  if (err) console.log(err);
  console.log(`listening to port ${process.env.PORT}`);
});

Final Updated 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" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="container my-2">
      <div id="message" class="m-2"></div>
      <div class="d-flex flex-row justify-content-between bd-highlight my-3">
        <h1 class="h3 text-center">Enter the details</h1>
        <a href="/read">
          <button class="btn btn-light">See All</button>
        </a>
      </div>
      <form id="myForm">
        <div class="mb-3">
          <label class="form-label">Name</label>
          <input type="text" class="form-control" name="name" id="formName" />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input
            type="email"
            class="form-control"
            name="email"
            id="formEmail"
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Phone</label>
          <input
            type="text"
            class="form-control"
            name="number"
            id="formNumber"
          />
        </div>

        <button type="submit" class="btn btn-danger enableOnInput">
          Submit
        </button>
      </form>
    </div>

    <script
      src="https://code.jquery.com/jquery-3.6.1.js"
      integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
      crossorigin="anonymous"
    ></script>

    <script>
      // name input validation
      function nameValidation() {
        $(".errormsg").remove();
        if ($("#formName").val() == "") {
          $("#formName").after(
            `<div class="errormsg">Name cant be empty!</div>`
          );
          return 0;
        }
        $(".errormsg").remove();
        return 1;
      }

      // email input validation
      function emailValidation() {
        $(".errormsg").remove();
        var emailData = $("#formEmail").val();
        var testEmail = /^[A-Z0-9._%+-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i;
        if (testEmail.test(emailData)) {
          $(".errormsg").remove();
          return 1;
        } else {
          $("#formEmail").after(
            `<div class="errormsg">Email cant be empty!</div>`
          );
          return 0;
        }
      }

      // number input validation
      function numberValidation() {
        $(".errormsg").remove();
        var a = $("#formNumber").val();
        var filter = /^[0-9-+]+$/;
        if (filter.test(a)) {
          if (a.length != 10) {
            $("#formNumber").after(
              `<div class="errormsg">Please enter 10 digit number</div>`
            );
            return 0;
          }
          $(".errormsg").remove();
          return 1;
        } else {
          $("#formNumber").after(
            `<div class="errormsg">Please enter correct phone number</div>`
          );
          return 0;
        }
      }

      function buttonFade() {
        $(".enableOnInput").prop("disabled", true);
        if (
          nameValidation() == 1 &&
          emailValidation() == 1 &&
          numberValidation() == 1
        ) {
          $(".enableOnInput").prop("disabled", false);
        }
      }

      $(document).ready(function () {
        buttonFade();

        $("#formName").keyup(function () {
          nameValidation();
          buttonFade();
        });

        $("#formEmail").keyup(function () {
          emailValidation();
          buttonFade();
        });

        $("#formNumber").keyup(function () {
          numberValidation();
          buttonFade();
        });

        $("#myForm").submit(function (e) {
          e.preventDefault();
          var ajaxData = $(this).serialize();
          console.log("data on click..", ajaxData);

          $.ajax({
            type: "POST",
            url: "/create",
            data: ajaxData,
            // data: {
            //   name: $("#formName").val(),
            //   email: $("#formEmail").val(),
            // },
            // async : flase,
            // dataType: "json",
            // encode: true,
            success: function (response, status) {
              console.log("response....", response, status);
              if (response.status == 200) {
                $("#message").html(
                  `<div class="alert alert-success" role="alert">${response.message}</div>`
                );
                setTimeout(() => {
                  window.location.replace(response.url);
                }, 2000);
              } else {
                $("#message").html(
                  `<div class="alert alert-danger" role="alert">${response.message}</div>`
                );
              }
            },
            error: function (err, status) {
              console.log("status - ", status);
              console.log(err, status);
              $("#message").html(
                `<div class="alert alert-danger" role="alert">Something went wrong.</div>`
              );
            },
          });
        });
      });
    </script>
  </body>
</html>

Conclusion –

In this article we have covered –

  • Complete Crud operation using Ajax
  • Updating a part of page without refreshing the entire page
  • Template engine i.e ejs to dynamically render UI component
  • Node and express js for creating end point and interaction with db’
Help Others

Himanshu Shekhar

Hey. I am a software engineer with 3+ years. Along with my job, I create content regarding coding and software engineering in general. I would request you to follow my blog and my video, which will help you in your software engineering journey. Follow me on Youtube →
View All Articles

Leave a Reply

Your email address will not be published.