Express.js Integration

How to use Bootstrap 5 with Express.js - free starter

This guide will provide you with a free template for a Express.js application, with MongoDB database and Bootstrap 5 front-end.


Prerequisites

Before starting the project make sure to install the following utilities:


Creating a new Express.js application

Let's create a fresh Express application so that we can go through all the steps together. For this tutorial we'll be using MongoDB database.

Step 1

Creating Node.js API with Express and Mongoose.

Mongoose is a MongoDB object modeling tool. It helps managing MongoDB access and models. You can initialize a MDB GO starter that already has configured Mongoose.

Note: If you don't have MDB CLI installed yet, you can do it with NPM: npm install -g mdb-cli. Now log in with your MDB account, type: mdb login. If you don't have account yet you can create one using mdb register command.

        
            
      mdb backend init node-free-express-mongoose
    
        
    

Step 2

Creating a MongoDB database.

In order to create a new database you need to run the following command.

        
            
          mdb database init -db mongodb
        
        
    
  • Create a new user
  • Provide username, password, database name and description.

CLI will display your username, password, database name and connections string. You will need this data in the next step. Now you can go to phpMyAdmin where you will be able to handle the administration of the MongoDB database.

Note: the password must contain at least one uppercase letter, one lowercase letter, one number, one special symbol and have minimum length of 8.

Step 3

Open the .env file located in the project directory and edit the DB_CONNECTION_STRING value. You should paste the connection string that's been shown to you after creating database. Edited file should look like this:

        
            
        PORT=3000
        DB_CONNECTION_STRING=YOUR_CONNECTION_STRING

      
        
    

Step 4

Install additional dependencies:

        
            
      npm install -D cors dotenv
    
        
    

Step 5

Add some config to the index.js

        
            
      "use strict";

      require("dotenv").config();
      const cors = require("cors");
      const express = require("express");
      const bodyParser = require("body-parser");
      const mongoose = require("mongoose");
      const { port, mongoUrl } = require("./app/config");

      const app = express();

      app.use(bodyParser.urlencoded({ extended: true }));
      app.use(bodyParser.json());
      app.use(cors());
      app.use(require("./app/routes"));
      app.use(express.static("public"));

      mongoose.connect(mongoUrl, {
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useFindAndModify: false,
        useCreateIndex: true,
      });

      const db = mongoose.connection;
      db.on("error", (err) => console.error("MongoDB connection error:", err));
      db.once("open", () =>
        app.listen(port, () => console.log(`Listening on port: http://localhost:${port}`))
      );
 
    
        
    

To Do App with MDB

Step 1

We need to modify the routes/index.js and routes/tasks.js

        
            
      "use strict";

      const express = require("express");
      const router = express.Router({ mergeParams: true });

      router.use("/", require("./tasks"));

      module.exports = router;
    
        
    
        
            
      "use strict";

      const express = require("express");
      const router = express.Router({ mergeParams: true });
      const Task = require("../models/task");
      const path = require("path");

      const findTasks = (request, response) => {
        Task.find({}, (err, res) => {
          if (err) {
            console.error(err);
            return response.sendStatus(500);
          } else if (!request.query.dataOnly) {
            response.sendFile(path.resolve("public", "index.html"));
          } else {
            response.send(res);
          }
        });
      };

      router.get("/", (request, response) => {
        findTasks(request, response);
      });

      router.get("/:id", (request, response) => {
        const { id } = request.params;
        Task.findOne({ _id: id }, (err, res) => {
          if (err) {
            console.error(err);
            return response.sendStatus(500);
          }
          response.send(res);
        });
      });

      router.post("/", (request, response) => {
        const { name, desc } = request.body;

        if (!name || !desc) return response.sendStatus(400);

        const task = new Task({ name, desc });
        task
          .save()
          .then(() => findTasks(request, response))
          .catch((e) => {
            console.error(e);
            response.sendStatus(500);
          });
      });

      router.put("/:id", async (request, response) => {
        const { id } = request.params;
        const { name, desc } = request.body;

        if (!name && !desc) return response.sendStatus(400);

        Task.findOneAndUpdate({ _id: id }, { name, desc })
          .then((res) => response.send(res))
          .catch((e) => {
            console.error(e);
            response.sendStatus(500);
          });
      });

      router.delete("/:id", async (request, response) => {
        const { id } = request.params;

        Task.deleteOne({ _id: id })
          .then(() => response.send())
          .catch((e) => {
            console.error(e);
          });
      });

      module.exports = router;

    
        
    

Step 2

Add new content to the index.html file inside the public directory.

Note: Remember to replace API_URL value with your application URL. If it's not assigned yet, run mdb backend publish -p node12. By default it's like https://USERNAME-PROJECT_NAME.mdbgo.io. For testing purposes you could use 'http://localhost:3000'

        
            
      <!DOCTYPE html>
      <html>
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
            <meta http-equiv="x-ua-compatible" content="ie=edge" />
            <title>Material Design for Bootstrap</title>
            <!-- MDB icon -->
            <link rel="icon" href="assets/img/mdb-favicon.ico" type="image/x-icon" />
            <!-- Font Awesome -->
            <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"/>
            <!-- Google Fonts Roboto -->
            <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
            <!-- MDB -->
            <link rel="stylesheet" href="assets/css/mdb.min.css" />
        </head>
        <body>
          <div class="container mt-5">
            <div class="row pt-5">
              <div class="col text-center">
                <button class="btn btn-primary" data-mdb-modal-init data-mdb-target="#addTaskModal" data-mdb-ripple-init>ADD TASK</button>
              </div>
            </div>
            <div class="row mt-3 p-5" style="min-height: 40vh;">
              <div class="col d-flex justify-content-center align-items-center">
                <ul class="list-group list-group-light" style="min-width: 22rem;">
                </ul>
              </div>
            </div>
            <div class="modal fade" id="addTaskModal" tabindex="-1" aria-labelledby="addTaskModalLabel"
            aria-hidden="true">
              <div class="modal-dialog">
                <div class="modal-content">
                  <div class="modal-header">
                    <h5 class="modal-title" id="addTaskModalLabel">Add task</h5>
                    <button type="button" class="btn-close" data-mdb-dismiss="modal" aria-label="Close"></button>
                  </div>
                  <div class="modal-body">
                    <form id="addTaskForm" action="/" method="post">
                      <div class="form-outline mb-4" data-mdb-input-init>
                        <input name='name' type="text" id="nameInput" class="form-control" />
                        <label class="form-label" for="form7Example1">Name</label>
                      </div>
                      <div class="form-outline mb-4" data-mdb-input-init>
                        <input name="desc" type="text" id="descInput" class="form-control" />
                        <label class="form-label" for="form7Example2">Email address</label>
                      </div>
                    </div>
                    <div class="modal-footer">
                      <button type="button" class="btn btn-secondary" data-mdb-dismiss="modal" data-mdb-ripple-init>
                        Close
                      </button>
                      <button type="button" class="btn btn-primary modalConfirmBtn" data-mdb-ripple-init>Confirm</button>
                    </div>
                  </form>
                </div>
              </div>
            </div>

            <div class="modal fade" id="editTaskModal" tabindex="-1" aria-labelledby="editTaskModalLabel"
            aria-hidden="true">
              <div class="modal-dialog">
                <div class="modal-content">
                  <div class="modal-header">
                    <h5 class="modal-title" id="editTaskModalLabel">Edit Task</h5>
                    <button type="button" class="btn-close" data-mdb-dismiss="modal" aria-label="Close"></button>
                  </div>
                  <div class="modal-body">
                    <form id="editTaskForm" action="/" method="post">
                      <div class="form-outline mb-4" data-mdb-input-init>
                        <input name='name' type="text" id="editNameInput" class="form-control" />
                        <label class="form-label" for="editNameInput">Name</label>
                      </div>

                      <div class="form-outline mb-4" data-mdb-input-init>
                        <input name="desc" type="text" id="editDescInput" class="form-control" />
                        <label class="form-label" for="editDescInput">Email address</label>
                      </div>
                    </div>
                    <div class="modal-footer">
                      <button type="button" class="btn btn-secondary" data-mdb-dismiss="modal" data-mdb-ripple-init>
                        Close
                      </button>
                      <button type="button" class="btn btn-primary modalConfirmBtn" data-mdb-ripple-init>Confirm</button>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </body>

        <!-- MDB -->
        <script type="text/javascript" src="assets/js/mdb.umd.min.js"></script>

        <!-- Custom scripts -->
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

        <script type="text/javascript">
          let taskToEditID
          let taskList
          let editTaskModalInstance;
          const API_URL = 'YOUR_API_URL';
          const modalConfirmBtn = document.querySelector('.modalConfirmBtn');
          const editTaskModal = document.querySelector('#editTaskModal');
          const addTaskModal = document.querySelector('#addTaskModal');
          const modalEditConfirmBtn = editTaskModal.querySelector('.modalConfirmBtn');
          const addTaskForm = document.querySelector('#addTaskForm');
          const editTaskForm = document.querySelector('#editTaskForm');
          const addTaskModalInstance = mdb.Modal.getInstance(document.querySelector('#addTaskModal'))

          const createAndReloadTasks = (taskData) => {
            const taskListElement = document.querySelector('ul')
            taskListElement.innerHTML = ''
            for (let i = 0; i < taskData.length; i++) {
              const taskToAdd =
              `<li class="list-group-item d-flex justify-content-between align-items-center gap-5">
                <div>
                  <div class="fw-bold">${taskData[i].name}</div>
                  <div class="fw-bold">${taskData[i].desc}</div>
                </div>
                <div>
                  <a class="editBtn" id="${taskData[i]._id}" style="cursor: pointer;">
                    <span class="fas fa-pen text-primary me-3" title="edit" data-mdb-modal-init data-mdb-target="#editTaskModal"></span>
                  </a>
                  <a class="deleteBtn" id="${taskData[i]._id}" style="cursor: pointer;">
                    <span class="fas fa-trash text-danger" title="delete"></span>
                  </a>
                </div>
              </li>`
              taskListElement.insertAdjacentHTML('beforeend', taskToAdd)
            }
            const deleteBtns = document.querySelectorAll('.deleteBtn');
            const editBtns = document.querySelectorAll('.editBtn');

            deleteBtns.forEach((btn)=> {
              btn.addEventListener('click', ()=> {
                deleteTask(btn.id)
              })
            })

            editBtns.forEach((btn)=> {
              btn.addEventListener('click', ()=> {
                openEditModal(btn.id);
                taskToEditID = btn.id;
              })
            })
          }

          const openEditModal = (id) => {
            axios.get(`${API_URL}/${id}`).then((res) => {
              const taskData = res.data;
              editTaskForm.querySelector('#editNameInput').value = taskData.name;
              editTaskForm.querySelector('#editDescInput').value = taskData.desc;
            })
              editTaskModalInstance = mdb.Modal.getInstance(document.querySelector('#editTaskModal'))
          }

          const getTasks = (dataOnly) => {
            axios({url:API_URL, method: 'GET', params: {dataOnly: dataOnly}}).then((res) => {
              taskList = res.data
              if (true){
                createAndReloadTasks(taskList)
              }
            });
          };

          const deleteTask = (id) => {
            axios.delete(`${API_URL}/${id}`).then((res) => {
              getTasks(true)
            });
          }

          const addNewTask = () => {
            let formDataObject = {}
            const formData = new FormData(addTaskForm)

            formData.forEach((value, key) => {
              formDataObject[key] = value;
            });

            axios({url: API_URL, method: 'POST', data: formDataObject}).then((res, err) => {
              getTasks(true)
            }).catch((err) => {
              console.log(err)
            })
            addTaskForm.reset()
          }

          modalEditConfirmBtn.addEventListener('click', () => {
            const editedData = {
              name: editTaskForm.querySelector('#editNameInput').value,
              desc: editTaskForm.querySelector('#editDescInput').value
            }

            axios({url: `${API_URL}/${taskToEditID}`, method: 'PUT', data: editedData}).then((res, err) => {
              getTasks(true)
            }).catch((err) => {
              console.log(err)
            })
            editTaskModalInstance.hide()
          })

          modalConfirmBtn.addEventListener('click', () => {
            addNewTask()
            addTaskModalInstance.hide()
          })
          
          getTasks(true)
        </script>
      </html>
    
        
    

If you haven't done it already, run npm start in your terminal. The app should be fully functional and should work correctly with backend.


Publish your app

Now you can publish your project on MDB GO using the following command:

Note: If you don't have MDB CLI installed yet, you can do it with NPM: npm install -g mdb-cli. Now log in with your MDB account, type: mdb login. If you don't have account yet you can create one using mdb register command.

        
            
        mdb backend publish -p node12
    
        
    

Note Since we need to install dependencies and run your app, it may take a few moments until it will be available under the provided URL.

Express will be able to reponse for requests with data from the database using following endpoints:

  • GET /tasks
  • GET /tasks/:id
  • POST /tasks
  • PUT /tasks/:id
  • DELETE /tasks/:id

Optimization

If you want to further optimize your application please visit:


Backend features

Express.js:

This example was created with use of Express.js. By using our Express.js + MongoDB API starter we got a ready environment to start a new project.


Frontend features

MDB UI KIT:

To create the project we used our ui kit, with which we can build basic views very quickly.

Views and Layouts:

In this project we have successfully integrated the Express.js + MongoDB API starter with the MDB Standard package. We created simple layout using MDB components and we used those components to send request such as get, post, put and delete requests to the database.