In the post ExpressJS MVC CRUD Application + MySQL + EJS we saw how to create an Express MVC CRUD application with MySQL as database and EJS as template engine. In this tutorial we'll see how to create Express MVC CRUD application with MongoDB as database and EJS as template engine. Bootstrap 5 is used for styling.
Note that this is not an Express REST application, it is an MVC application using HTML form which uses methods POST and GET.
Prerequisite
To create this application NodeJS, ExpressJS and MongoDB should be installed in the system.
Installing MongoDB driver for NodeJS
You can install the official MongoDB driver for NodeJS by running the following command from the root directory of your application.
npm install mongodb
Creating DB Connection
In the mongodb driver there is a MongoClient class which is used to connect to MongoDB server. DB connected is 'knpcode', there is no need to have that DB instance already created. MongoDB will create the DB with the given name if it doesn't exist.
util\dbconfig.js
const {MongoClient} = require('mongodb'); let dbconnection; const url = 'mongodb://127.0.0.1:27017'; const client = new MongoClient(url); const mongodbConnect = async () => { console.log('mongodbConnect'); try{ await client.connect(); console.log('Connected successfully to server'); dbconnection = client.db('knpcode'); }catch(error){ console.log(error); throw error; } } const getDBConnection = async() => { if(dbconnection){ console.log('in if getDBConnection') return dbconnection; }else{ console.log('in else getDBConnection') await mongodbConnect(); return dbconnection; } } exports.getDBConnection = getDBConnection;
In the file there are two functions mongodbConnect and getDBConnection. Using getDBconnection function it is checked if the connection already exists then the connection is reused only if it is not yet created mongodbConnect function is called to create DB connection. In other classes where MongoDB connection is needed, getDBConnection function will be called, that's why it is exported.
Express MVC CRUD example with MongoDB
In this example User is used as Model class.
models\user.js
module.exports = class User { constructor(firstName, lastName, userType, startDate){ // MongoDB adds _id that will be used as id //this.id = id; this.firstName = firstName; this.lastName = lastName; this.userType = userType; this.startDate = startDate; } }
Table in MySQL
CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `first_name` varchar(45) NOT NULL, `last_name` varchar(45) NOT NULL, `user_type` varchar(15) NOT NULL, `start_date` date DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb3
In the example we'll create an application where root route ('/) should display all user data along with Edit and Delete button. On clicking Edit a modal with the data for the corresponding user should open and clicking Delete button should delete that particular User data.
A link to add user is also provided, clicking that link should display a form to enter user data.
Route definition
When creating an Express MVC application, convention is to keep route class light. Logic for the routes is kept in the Controller class. Route definition should refer the function in Controller.
routes\user.js
const express = require('express'); const router = express.Router(); const userController = require('../controllers/user'); //to show user data router.get('/', userController.getUsers) // show user form router.get('/user', userController.showUserForm) // Insert user and edit user router.post('/adduser', userController.saveUser) // delete user router.post('/deleteuser', userController.deleteUser) module.exports = router;
As you can see Controller is imported here and the function in the Controller are called for processing when corresponding paths are accessed.
Controller
controllers\user.js
const getDBConnection = require('../util/dbconfig').getDBConnection; const ObjectId = require('mongodb').ObjectId; const User = require('../models/user') exports.getUsers = async (req, res) => { console.log('In getUsers user Controller'); try{ const dbConnection = await getDBConnection(); const cursor = dbConnection.collection('user').find().sort({firstName:1}); // Retreive all the results in an array const results = await cursor.toArray(); res.render('user/displayUser', { users: results, pageTitle: 'Users Data', userType: ['Silver', 'Gold', 'Platinum'] }) }catch(err){ console.log(err); } } exports.showUserForm = (req, res) => { console.log('In showUserForm user Controller'); res.render('user/userform', { pageTitle: 'User Form', userType: ['Silver', 'Gold', 'Platinum'] }) } exports.saveUser = async (req, res) => { console.log('In saveUser user Controller'); const id = req.body.id; const firstName = req.body.firstName; const lastName = req.body.lastName; const userType = req.body.userType; const startDate = req.body.startDate; const user = new User(firstName, lastName, userType, startDate); try{ const dbConnection = await getDBConnection(); // id present so update if(id != undefined){ await dbConnection.collection('user').updateOne( {_id: ObjectId.createFromHexString(id)}, { $set: user } ); }else{ // no id means insert await dbConnection.collection('user').insertOne(user); } }catch(err){ console.log(err); } console.log('redirecting....') res.redirect('/'); } exports.deleteUser = async (req, res) => { console.log('In deleteUser user Controller'); const id = req.body.deleteid; console.log('UserID ', id) try{ const dbConnection = await getDBConnection(); console.log('.... Waiting for connection'); await dbConnection.collection('user').deleteOne({ _id: ObjectId.createFromHexString(id) }) res.redirect('/'); }catch(err){ console.log(err); } }
Important points about the logic here-
- In the beginning there are imports, one is for getDBConnection function in DBConfig.js, ObjectId is class representation of the BSON ObjectId type this is needed to create _id in correct format for storing in MongoDB. User model is also imported to create User object using the passed user data.
- Note that the collection with in the DB is ‘user’.
- First function getUsers() is used to get all users from DB. To query all documents, collection’s find() method is used. User data is also sorted in ascending order on the basis of firstName.
- Note that find() returns a cursor to the documents that match the query criteria. Using Cursor’s toArray() function the
results are retrieved in an array.
This array (containing User objects) is passed to the EJS template. User type is a dropdown, options for that are also passed. - showUserForm() function is used to display a form that is used to enter and save new user.
- saveUser() function is used for both inserting and updating data. If userID is present that means an update otherwise insert. Form data is received by accessing request.body method. body-parser module is used to parse form data sent by a user by submitting a web form.
- For updating collection’s updateOne() method is used where the selection criteria is based on matching IDs. Complete User object (containing modified data for the properties) is passed for updating data.
- For inserting collection’s insertOne() method is used where User object is passed.
- Once updation or insertion is done, redirection to root route ‘/’ is done which triggers the execution of getUsers() function in the controller.
- deleteUser() function is used to delete user from DB. User id is passed from the view in a hidden field which is accessed in the deleteUser() function to create Delete query. Collection’s deleteOne() method is used for deleting user, deletion criteria is based on matching IDs.
Views
Views are created using EJS template engine.
views\user\displayUser.ejs
<!DOCTYPE html> <html> <head> <title><%= pageTitle %></title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script> function editData(user){ // Converting to object const temp = JSON.parse(user); // setting values in modal box form - EDIT document.getElementById("id").value = temp._id; document.getElementById("firstName").value = temp.firstName; document.getElementById("lastName").value = temp.lastName; document.getElementById("startDate").value = temp.startDate; document.getElementById("userType").value = temp.userType; } function deleteData(id){ document.getElementById("deleteid").value = id; } </script> </head> <body> <div class="container"> <a href="/user">Add User</a> <h2 class="text-info d-flex justify-content-center mb-3">User Data</h2> <table class="table table-striped table-bordered table-sm"> <thead> <tr> <th>id</th> <th>First Name</th> <th>Last Name</th> <th>User Type</th> <th>Start Date</th> </tr> </thead> <% for(let user of users) { %> <tr> <td><%= user._id%></td> <td><%= user.firstName%></td> <td><%= user.lastName%></td> <td><%= user.userType%></td> <td><%= user.startDate%></td> <td class="text-center"><button type="button" class="btn btn-primary me-3" data-bs-toggle="modal" data-bs-target="#editModal" onclick="editData('<%= JSON.stringify(user)%>')"> Edit </button> <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal" onclick="deleteData('<%= user._id%>')"> Delete </button> </td> </tr> <%}%> </table> <!-- Edit and Delete Modal--> <div class="modal fade" id="editModal" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="editModalLabel">Modify User</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form action="/adduser" method="POST"> <div class="mb-3 row"> <label for="id" class="form-label" ></label>User ID</label> <input type="text" class="form-control" name="id" id="id" readonly/> </div> <div class="mb-3 row"> <label for="firstName" class="form-label" ></label>Enter First Name</label> <input type="text" class="form-control" name="firstName" id="firstName" /> </div> <div class="mb-3 row"> <label for="lastName" class="form-label">Enter Last Name</label> <input type="text" class="form-control" name="lastName" id="lastName" /> </div> <div class="mb-3 row"> <label for="userType" class="form-label">User Type</label> <select class="form-select" name="userType" id="userType"> <% for(let type of userType) {%> <option value="<%=type%>"><%=type%></option> <% } %> </select> </div> <div class="mb-3 row"> <label for="startDate" class="form-label">Enter Start Date</label> <input type="date" class="form-control" name="startDate" id="startDate" /> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="submit" class="btn btn-primary">Save changes</button> </div> </form> </div> </div> </div> </div> <div class="modal fade" id="deleteModal" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="deleteModalLabel">Delete User</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form action="/deleteuser" method="POST"> <p>Do you really want to delete the user</p> <input type="hidden" class="form-control" name="deleteid" id="deleteid"/> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="submit" class="btn btn-primary">OK</button> </div> </form> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"> </script> </body> </html>
Important points about this view-
- Bootstrap is used for styling, css for the same is imported from the CDN.
- This same view code is used for showing user data in a table, modifying existing user and deleting user.
- Clicking Edit or Delete button opens a Modal box. In case of edit, existing user data is pre-populated, for that JavaScript function editData() is used. In case of delete id is populated in a hidden field, for that JavaScript function deleteData() is used.
views\user\userform.ejs
This is the EJS template for showing user form, where new user data is entered that can be then saved in DB.
<!DOCTYPE html> <html> <head> <title><%= pageTitle %></title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> </head> <body> <div class="container"> <form action="/adduser" method="POST"> <div class="mb-3 row"> <label for="firstName" class="form-label" ></label>Enter First Name</label> <input type="text" class="form-control" name="firstName" id="firstName" /> </div> <div class="mb-3 row"> <label for="lastName" class="form-label">Enter Last Name</label> <input type="text" class="form-control" name="lastName" id="lastName" /> </div> <div class="mb-3 row"> <label for="userType" class="form-label">User Type</label> <select class="form-select" name="userType" id="userType"> <% for(let type of userType) {%> <option value="<%=type%>"><%=type%></option> <% } %> </select> </div> <div class="mb-3 row"> <label for="startDate" class="form-label">Enter Start Date</label> <input type="date" class="form-control" name="startDate" id="startDate" /> </div> <button type="submit" class="btn btn-primary">Save</button> </form> </div> </body> </html>
App.js
This JS file acts as a starting point.
const express = require('express'); const app = express(); const bodyParser = require('body-parser'); const port = 3000; app.set('view engine', 'ejs'); app.set('views', 'views'); const userRoutes = require('./routes/user'); app.use(bodyParser.urlencoded({ extended: false })); app.use(userRoutes); app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
With all the changes done you can run the application by using the command.
node app.js
On accessing http://localhost:3000/ all the users are displayed.
Edit user data
Delete User
Add user
That's all for the topic ExpressJS MVC CRUD Application + MongoDB + EJS. If something is missing or you have something to share about the topic please write a comment.
You may also like
- Express Route Parameters - Creating Dynamic URL
- ExpressJS Query Parameters
- How to Add 404 Error Page in Express.js
- ExpressJS: EJS Template Engine Integration With Examples
- Using express-generator to Create ExpressJS App Structure
- Java Stream Collectors.collectingAndThen() Examples
- Java instanceof Operator With Examples
- Java Program to Convert Numbers to Words
- React Form Creation and Validation Using React Hooks
- Spring Boot Properties File: @ConfigurationProperties Example