Securely upload and view image in Node JS and Mongo DB

Learn how to securely upload and view image in Node JS and Mongo DB. Most of the time when we create a website where we allow users to upload images, we face a problem to secure them from unauthorized access. For example, you are creating a website where you are allowing users to upload pictures and share them with only selected people. Now you do not want any other person to view that picture.

This can be done easily with Node JS. First, we are going to create an empty project. So run the following commands in your terminal.

> npm init
> npm install express express-formidable ejs fs mongodb
> npm install -g nodemon
> nodemon server.js

Upload image

After that, create a file named server.js and write the following code in it.

// server.js

const express = require("express")
const app = express()
app.set("view engine", "ejs")

const port = process.env.PORT || 3000
app.listen(port, function () {
    console.log("Server started.")

    app.get("/", function (request, result) {
      result.render("index")
    })
})

Now, we will create a folder named views and inside this folder, create a file named index.ejs. Following will be the content of this file.

<!-- views/index.ejs -->

<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="file" />
  <input type="submit" value="Upload" />
</form>

This will create a form where user can select image. You can access this from the URL:

http://localhost:3000

Now in our server.js file, we will include the file system module, express formidable module and connect Mongo DB.

// server.js

const fs = require("fs")
const mongodb = require("mongodb")
const ObjectId = mongodb.ObjectId
const mongoClient = new mongodb.MongoClient("mongodb://localhost:27017")

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

// Connect the client to the server (optional starting in v4.7)
await mongoClient.connect()

// Establish and verify connection
const db = await mongoClient.db("upload_view")
db.command({ ping: 1 })
console.log("Database connected")

After that, we will create a route that will handle this request.

// server.js

app.post("/upload", async function (request, result) {
  const file = request.files.file

  const fileData = await fs.readFileSync(file.path)
  if (!fileData) {
    console.error(fileData)
    return
  }

  const filePath = "uploads/" + (new Date().getTime()) + "-" + file.name
  fs.writeFileSync(filePath, fileData)

  await db.collection("files").insertOne({
    path: filePath,
    name: file.name,
    size: file.size
  })

  result.send("File has been uploaded.")
})

This will upload the file in your uploads folder. If you do not have that folder, you need to create it. This will also save the uploaded file data in Mongo DB.

You will not be able to view the images directly from the URL in the browser. So we need to create an API allowing users to view the image.

View image

First, we will fetch all the images from Mongo DB. So change your main route to the following:

// server.js

app.get("/", async function (request, result) {
  const files = await db.collection("files").find({}).toArray()
  result.render("index", {
    files: files
  })
})

Now, in your views/index.ejs, we will loop through all images and display their name and a link to view.

<!-- views/index.ejs -->

<% files.forEach(function (file) { %>
  <p>
    <a href="image/<%= file._id %>">
      <%= file.name %>
    </a>

    <img src="image/<%= file._id %>" style="width: 300px;" />
  </p>
<% }) %>

Finally, we need to create an API that will return the content of image. This will allow you to view the image on user side.

// server.js

app.get("/image/:_id", async function (request, result) {
  const _id = request.params._id
  const file = await db.collection("files").findOne({
    _id: new ObjectId(_id)
  })

  if (file == null) {
    result.json({
      status: "error",
      message: "File not found."
    })
    return
  }

  const fileData = await fs.readFileSync(file.path)
  if (!fileData) {
    console.error(fileData)
    return
  }
  result.writeHead(200, {
    "Content-Type": "image/png",
    "Content-Length": fileData.length
  })
  result.end(fileData)
})

Now you will be able to view the image as well.

How to create nested modules – Node JS

In this article, we are going to teach you, how you can have nested modules in your Node JS project. Nested modules help in splitting a large code-base into smaller files. This helps in debugging and scaling easily.

Video tutorial

Let’s say that you want to access the following URL:

http://localhost:3000/users/social

You want to have a “users” module where all user code will go. And you want the user’s module to be further divided into “social” module where all the code for user’s social networks will go, for example.

If you do not know how to create and set up a Node JS application, please check our this tutorial first.

So we will start by creating a user module. Create a new folder named “modules” at the root of your project. Then in this folder, create a file named “users.js”. The following code will go in the users.js file.

const express = require("express")

// [include social.js module]

module.exports = {
	init: function (app) {
		const userRouter = express.Router()

		userRouter.get("/", function (request, result) {
			result.send("Inside users.js")
		})

		app.use("/users", userRouter)

		// [call init method of social.js here]
	}
}

It creates a router object, named userRouter, from the express object. Then it tells the app to use this userRouter object for every route starting from “/users” in the URL. The init function accepts an app object which we will send from our main “server.js” file.

So go to your main “server.js” file and include the users module in it by writing the following line at the top.

const users = require("./modules/users")

const express = require("express")
const app = express()

Then, we need to call the init function of it and pass the app instance to it.

users.init(app)

If you access the following URL now, you will see a message “Inside users.js” in the browser:

http://localhost:3000/users

Now we need to create another file named “social.js” inside modules folder. The following will be the code for social.js file:

const express = require("express")

module.exports = {
	init: function (userRouter) {
		const socialRouter = express.Router()

		socialRouter.get("/", function (request, result) {
			result.send("Inside social.js")
		})

		userRouter.use("/social", socialRouter)
	}
}

This module’s init method accepts userRouter as an argument that we had created in our “users.js” file. And it is using that userRouter object instead of app object because this module will be used only when the “/social” path is appended after the “/users” path.

The final thing you need to do is to call the init method of the “social.js” file inside our “users.js” file. So go to your “users.js” file and first include the social module in it. Write the following line in the [include social.js module] section:

const social = require("./social")

Then write the following code in the [call init method of social.js here] section:

social.init(userRouter)

So if you access the following URL now, you will be able to see “Inside social.js” in your browser.

http://localhost:3000/users/social

We created an E-commerce application in Node JS and Mongo DB where we used nested modules to have admin > products and orders. You can check that project from here.

So that’s how you can have nested modules in Node JS express framework. If you face any problems in following this, kindly do let me know.

Source code – How to create nested modules in Node JS

[wpdm_package id=’1736′]

Compress image in Node JS

In this tutorial, we are going to teach you, how you can compress image in Node JS. You can download and install Node JS from their official site.

Video tutorial: Compress image in Node JS

First, create a new folder. Open CMD in it. And run the following commands:

> npm init
> npm install express http

Then, create a file named “server.js” and write the following code in it:

const express = require("express")
const app = express()
const http = require("http").createServer(app)

// [other modules]

const port = process.env.PORT || 3000

http.listen(port, function () {
	console.log("Server started running at port: " + port)

	// [post route goes here]

	// [get route goes here]
})

Then run the following commands in your terminal:

> npm install -g nodemon
> nodemon server.js

You will see a message saying the “Server started running at port: 3000”. You can access your server from the following URL:

http://localhost:3000

Now, we need to install the module for displaying views. And to get input from form fields. So, install the following modules:

> npm install ejs fs express-formidable

EJS module is used for displaying the HTML files. FS stands for File System, this module is used to handle files. And “express-formidable” is used for getting values from the input field. Make sure to run the “nodemon server.js” command again.

Install compress image module in Node JS

To install a module to compress image in Node JS, you need to run the following command in your terminal:

> npm install pngquant-bin@6.0.1 gifsicle@5.2.1 compress-images fs

Then, we need to include those modules in our project. So write the following lines in the [other modules] section:

const compressImages = require("compress-images")
const formidable = require("express-formidable")
app.use(formidable())

const fileSystem = require("fs")
app.set("view engine", "ejs")

Now, write the following lines in the [get route goes here] section:

app.get("/", function (request, result) {
	result.render("index")
})

After that, you need to create a folder named “views” at the root of your project. And in that folder, create a file named “index.ejs”. Following will be the code of “index.ejs”:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<title>Compress image</title>
	</head>

	<body>
		<form method="POST" action="/compressImage" enctype="multipart/form-data">
			<input type="file" name="image" accept="image/*" required />
			<input type="submit" value="Compress image" />
		</form>
	</body>
</html>

It will display a simple HTML form with an input field and a submit button. Then, you need to create a POST route that will handle this request. The following code goes in the [post route goes here] section of the “server.js” file:

app.post("/compressImage", function (request, result) {
    const image = request.files.image
    if (image.size > 0) {

        if (image.type == "image/png" || image.type == "image/jpeg") {
            fileSystem.readFile(image.path, function (error, data) {
                if (error) throw error

                const filePath = "temp-uploads/" + (new Date().getTime()) + "-" + image.name
                const compressedFilePath = "uploads/"
                const compression = 60
                
                fileSystem.writeFile(filePath, data, async function (error) {
                    if (error) throw error
                    
                    compressImages(filePath, compressedFilePath, { compress_force: false, statistic: true, autoupdate: true }, false,
                        { jpg: { engine: "mozjpeg", command: ["-quality", compression] } },
                        { png: { engine: "pngquant", command: ["--quality=" + compression + "-" + compression, "-o"] } },
                        { svg: { engine: "svgo", command: "--multipass" } },
                        { gif: { engine: "gifsicle", command: ["--colors", "64", "--use-col=web"] } },
                        async function (error, completed, statistic) {
                            console.log("-------------")
                            console.log(error)
                            console.log(completed)
                            console.log(statistic)
                            console.log("-------------")

                            fileSystem.unlink(filePath, function (error) {
                                if (error) throw error
                            })
                        }
                    )

                    result.send("File has been compressed and saved.")
                })

                fileSystem.unlink(image.path, function (error) {
                    if (error) throw error
                })
            })
        } else {
            result.send("Please select an image")
        }
    } else {
        result.send("Please select an image")
    }
})

The last thing you need to do is to create two folders named “uploads” and “temp-uploads” at the root of your project. Here, all compressed images will be stored.

This will compress the image up to 60%. You can now try to select an image from the input file and hit “Compress image”. You will see a success message if everything goes right. And you will see your compressed image in the “uploads” folder.

Real-world example

We implemented this compression in one of our projects, so you can get the real-world example of image compression in Node JS.

So that’s how you can compress an image in Node JS. If you face any problems in following this, kindly do let us know.

Download source code

[wpdm_package id=’1704′]

Single page application in MEVN stack – Realtime chat

In this article, we are going to provide you with a free course to develop a full-fledged real-time chat app in MEVN (MongoDB, Express, Vue JS, and Node JS) Stack. Here M stands for MongoDB which we have used as a database. E stands for Express which is a Node JS framework. V stands for Vue JS which will be our front end. We will be creating a single-page application in Vue JS. And N stands for Node JS which will be our backend.

You can download Node JS from here. And Mongo DB from their official site.

FeaturesFreePremium $100
Sign upYesYes
Login/logoutYesYes
Add contactsYesYes
Private chatYesYes
Message encryptionYesYes
Chat with attachmentYesYes
Realtime chatYesYes
Email verificationNoYes
Reset passwordNoYes
User profileNoYes
Chat with emojisNoYes
Bookmark messagesNoYes
Copy messageNoYes
Archive chat with passwordNoYes
Group chatNoYes
Notifications pageNoYes
Customer supportNoYes

We will do the deployment of this app on live servers. You can find the link to all the tutorials of this series here. Following are the topics we have covered in this series.

Installation of Node JS server

In the very first tutorial, we have done the installation of the Node JS server. We also connect our Node JS server with MongoDB. You can find the tutorial here.

Setup Vue JS CLI – A single page application

Then we set up our Vue JS app using CLI. We used Vue JS CLI because we will be creating a single page application. We will be using Vue 3. Right now, we will be using the app on the local server. But once it is done, we will teach you how you can create a production build of it. And also how you can make it live on your website. This tutorial can be found here.

Routing for single page application

As this is a single page application, so we will be doing routing using Vue components. You can learn how to implement routing in a single page Vue JS app from this tutorial.

Hello world

In this part, we will learn how to integrate Bootstrap into your Vue app. So you can design the theme as per your choice. We also show you how you can create header and footer layouts in Vue JS. The header and footer will be displayed on each page. For the header, we will be creating a simple Bootstrap navbar. And for the footer, we will simply be displaying copyright text and the current year dynamically.

Registration

You will learn how to display a form using Vue JS. Sends the form values to the Node JS server. And how Node JS can save the data in the MongoDB database. You will also learn how to check if certain data exists in MongoDB or not. Moreover, you will also learn how to encrypt the passwords before saving them in MongoDB. So even if your database gets stolen, your user’s passwords will remain unknown to the hacker.

One more thing you will learn in this part is how to deal with CORS (Cross-Origin Resource Sharing) errors in the Node JS server.

Email verification

After successful registration, an email will be sent to the user’s email address. That email will have the verification code. And the user will be redirected to a page where he can enter his verification code.

He will not be able to log in until he verifies his email address.

Login

In this part, we do the authentication. You will learn how to generate JWT (JSON Web Tokens) and how to store them in local storage for future usage. You will also learn how to validate the encrypted passwords saved in MongoDB while authenticating.

Reset Password

If a user forgets his password, he can reset it. He just needs to enter his email address. An email will be sent to him with a link to reset the password. When he clicks that link, he will be redirected to a page where he can enter his new password.

Get logged-in user’s data

We will learn how to call AJAX with headers in Vue JS. How Node JS server read the headers? And how to create middleware in Node JS.

Logout

We will teach you how to log out as a user from MongoDB and from the Vue JS client app. We will remove the JWT token we create in the login part.

Contacts

To chat with people, we need to add them to our contacts. We will be creating separate components in the Vue JS app for adding and displaying all contacts. We will also be creating a new module in the Node JS server. You will learn how to insert data in a nested array in MongoDB.

We will be calling AJAX requests using Axios in Vue JS. While deleting a contact, you will learn how to ask for confirmation using sweet alert dialogs.

You will also learn how to remove an element from a nested array in MongoDB.

Chat with attachments

This is the main module of this app. We advise you to please put more focus on this part. It teaches you how to import CSS in specific Vue components only. How to send input type file from the Vue JS app to the Node JS server. How Node JS saves files on the server.

Encryption and Decryption

We are saving messages in MongoDB in encrypted form. While fetching the messages, we are again decrypting the messages back to their original state. Even if someone has access to your database, he will not be able to read your messages. Because he does not have the key. The key will only be stored on the Node JS server.

You can see the messages are fully encrypted. On the home page, where we are displaying all user’s contacts, we will also be displaying unread messages from each contact.

Attachments uploaded with messages are also protected from direct URL access. So we will show you how you can convert a file in base64 string using Node JS. And download them to your system using the Vue JS app.

Realtime chat using Socket IO

Chat will be real-time. Meaning users do not have to refresh the page to see new messages. He will automatically receive a notification. If the chat is opened, then the incoming message will be appended automatically.

We have “load more” functionality. This means that we will only show 10 messages in each AJAX call. To get the previous 10 messages, the user will click on that button and we will fetch 10 more messages. So you will learn pagination using Node JS and Mongo DB too.

Deploy Node JS, Mongo DB, and Vue JS apps

We will be deploying our Node JS server on Heroku. You can check our Heroku guide here. We will be deploying MongoDB on mongodb.com. And we will be deploying our Vue JS app on any hosting provider we choose. For example, a2hosting, Bluehost, Siteground, etc. We will create a production-ready build of our Vue JS app and upload it on our live server.

However, you might face some problems going live. That’s why we have written a detailed complete tutorial on the deployment on all these 3 platforms.

Search Contact

You can search your contacts by their email address. Searching with their email address points you to the right person. Because multiple people can have the same name. But email is unique in the entire “users” collection in MongoDB.

Group chat

Previously we discussed private chat. In this tutorial series, we also cover the group chat feature. You will be able to create groups. Add members to the groups. You will be sending an invitation to the members to join the group. The other person can accept your request to join the group or can ignore it.

Any member can leave the group anytime he wants. But this is not the case for admins. For the admin to leave the group, he must first assign someone else to the admin of the group. The other person must be a member of the group. After assigning someone else as an admin, he can easily leave the group.

People who are not members or the admin of the group, cannot send or view the messages in the group chat.

The chat in the group is also real-time. All the other members of the group will get a notification when someone sends a message. If they have opened the group, then the new message will automatically get appended at the end of the chat.

Group chat is also end-to-end encrypted.

And same as we did for private chat, we are displaying several unread messages from a group on the page where we are displaying all groups.

Emojis

In chat, you can also send emojis to the recipient. Emojis are also encrypted.

Bookmark message

You can also bookmark your favorite messages. This way, you can always see your bookmarked messages whenever you want. You can bookmark your messages as well.

Copy message

If someone sends you a lengthy message, like an essay. You can copy the whole message with a single click.

Archive chat with password

  • You can archive your private chat with a password.
  • Once archived, no one will be able to view the chat.
  • To view the chat, you must provide the correct password.
  • You can remove the password by simply entering the correct password.
  • Archived chats cannot be deleted. You must first un-archive the chat, then delete it.

Re-designed

I re-designed the website into a beautiful template. That’s how it looks now.

So you will be learning to create a full-stack chat app from scratch using Node JS, Mongo DB, and Vue JS. Again, you can find the tutorial for the complete series here. Enjoy!

Free version

https://github.com/adnanafzal565/spa-chat-app-mevn

Our Trustpilot reviews

Encrypt and Decrypt Strings using Node JS and Mongo DB

In this tutorial, we are going to teach you, how you can encrypt and decrypt the strings using Node JS and Mongo DB. The encrypted strings will be saved in the database and upon retrieval will be decrypted.

Setup the Project

First, create an empty folder and open a command prompt in it. Then run the following commands in it one by one:

npm init
npm install express http mongodb crypto ejs
npm install -g nodemon
nodemon server.js

crypto module will be used to encrypt and decrypt the strings. ejs module will be used to render HTML files. Then create a file named server.js at the root of folder. Open server.js file and write the following code in it:

// initialize express JS
const express = require("express");
const app = express();

// create HTTP server
const http = require("http").createServer(app);

// [include Mongo DB module here]

// start the server
http.listen(process.env.PORT || 3000, function () {
    console.log("Server started running...");

    // [connect with Mongo DB here]
});

Open your terminal and you will see the message that the server has been started. You can access your project at http://localhost:3000/

Setup Mongo DB

Write the following lines in place of [include Mongo DB module here] section in your server.js file:

// include mongo DB
var mongodb = require("mongodb");
var MongoClient = mongodb.MongoClient;

Then connect the database in the [connect with Mongo DB here] section:

// connect with mongo DB server
MongoClient.connect("mongodb://localhost:27017", function (error, client) {
    if (error) {
        console.error(error);
        return;
    }

    // set database
    db = client.db("encrypt_decrypt_string");
    console.log("Database connected");

    // [routes goes here]

});

Save the file and open your terminal, now you will see another message that the database has been connected as well.

Encrypt the String

First, include the crypto module at the top of your server.js file:

// include crypto module
const crypto = require("crypto")

// set encryption algorithm
const algorithm = 'aes-256-cbc'

// private key
const key = "adnan-tech-programming-computers" // must be of 32 characters

// random 16 digit initialization vector
const iv = crypto.randomBytes(16)

The key and initialization vector can be any string of 32 and 16 characters respectively. But it is recommended to keep the key hard-coded and initialization vector to be random. Because IV will be saved in database.

We cannot save the key in the database, because if the database gets hacked then all your encrypted strings will be compromised as well.

After that, create a GET route that will encrypt the string and save in Mongo DB. Write the following code in the [routes goes here] section:

// route to encrypt the message
app.get("/encrypt/:message", async function (request, result) {
    // get message from URL
    const message = request.params.message;

	// random 16 digit initialization vector
	const iv = crypto.randomBytes(16);

    // encrypt the string using encryption algorithm, private key and initialization vector
    const cipher = crypto.createCipheriv(algorithm, key, iv);
    let encryptedData = cipher.update(message, "utf-8", "hex");
    encryptedData += cipher.final("hex");

    // convert the initialization vector to base64 string
    const base64data = Buffer.from(iv, 'binary').toString('base64');
    
    // save encrypted string along wtih initialization vector in database
    await db.collection("strings").insertOne({
        iv: base64data,
        encryptedData: encryptedData
    });

    // show the encrypted message
    result.send(encryptedData);
});

Save the file and try to access the URL: http://localhost:3000/encrypt/This is a test string

You can write any string in the URL and you will see the encrypted text in the browser and also in your Mongo DB.

Show all Encrypted Strings

To show all encrypted strings, we are going to use a module named ejs. Write the following line at the top of your server.js file:

// set templating engine as EJS
app.set("view engine", "ejs");

The create the following GET route to show all encrypted strings:

// route to show all encrypted messages
app.get("/", async function (request, result) {
    // get all data from database
    const data = await db.collection("strings")
        .find({})
        .sort({
            _id: -1
        }).toArray();

    // render index.ejs
    result.render("index", {
        data: data
    });
});

Create a new folder named views and inside it create a file named index.ejs. It will have the following code:

<table>
    <% for (let d in data) { %>
        <tr>
            <td>
                <a href="/decrypt/<%= data[d].encryptedData %>" target="_blank">
                    <%= data[d].encryptedData %>
                </a>
            </td>
        </tr>
    <% } %>
</table>

Access the URL: http://localhost:3000/ and you will see all your encrypted strings in descending order.

Decrypt the String

To decrypt the string, create a GET route and fetch the encrypted string and initialization vector from Mongo DB. Then we can apply the decryption using key.

// route to decrypt the message
app.get("/decrypt/:encrypted", async function (request, result) {
    // get encrypted text from URL
    const encrypted = request.params.encrypted;

    // check if text exists in database
    const obj = await db.collection("strings").findOne({
        encryptedData: encrypted
    });

    if (obj == null) {
        result.status(404).send("Not found");
        return;
    }

    // convert initialize vector from base64 to buffer
    const origionalData = Buffer.from(obj.iv, 'base64') 

    // decrypt the string using encryption algorithm and private key
    const decipher = crypto.createDecipheriv(algorithm, key, origionalData);
    let decryptedData = decipher.update(obj.encryptedData, "hex", "utf-8");
    decryptedData += decipher.final("utf8");

    // display the decrypted string
    result.send(decryptedData);
});

If you click on any string now, you will see the actual string.

[wpdm_package id=’1554′]

Picture Competition Web App – Node JS, Mongo DB, Vue JS

Picture Competition is a mobile responsive real-time web application developed in Node JS and Mongo DB. Its frontend is designed in Bootstrap and Vue JS. You can create competition between 2 people and the others can vote on the person based on looks or skills etc.

Demo:

https://github.com/adnanafzal565/picture-competition-web-app-node-js

FeaturesFreePremium $100
Login and registrationYesYes
Create competitionsYesYes
Search & sortYesYes
Vote on CompetitionYesYes
Delete CompetitionYesYes
Reset PasswordNoYes
Email VerificationNoYes
Adult Image ValidationNoYes
SMTP Configurations Admin PanelNoYes
Real-time VotesNoYes
Real-time Admin Panel StatisticsNoYes
Real-time CommentsNoYes
User ProfileNoYes
Realtime update via socketsNoYes
NotificationsNoYes
Load more buttonNoYes
Admin panelNoYes
Manage competitionsNoYes
Free customer supportNoYes

Mongo DB Backend

competition’s collection

1. Login and Registration

It uses login authentication using JWT (JsonWebToken). It does not use the browser session due to the following reasons:

  1. Sessions are destroyed once your app is restarted from terminal.
  2. Sessions are not linked with the server, they are stored in browser only.

The benefit of using JWT is that you can always log out a user from your Mongo DB. Just go to the “users” collection and find the specific user. Then empty the “accessToken” field of that user’s document.

Login and Registration

2. Create Competitions

Registered users can create competitions between 2 users. You can enter name and upload 1 picture of each competitor. There is no limit in the number of competitions to create, you can create as many as you want. You can view your created competitions on your “My Competitions” page.

As soon as you create a competition, it will automatically be displayed to all the users as well as to the admin in real-time. Users do not have to refresh the page to see new competitions. It uses Socket IO to display data in real-time.

Create Competition

3. Search

Users can search competitions by the name of competitors. Data is filtered and rendered using Vue JS. There is no need to press the enter key, the data is filtered as soon as the user starts typing in the search box.

Search and Sort

4. Sort

Users can also sort the competitions by any of the following for sorting functions:

  1. Highest votes to lowest.
  2. Lowest votes to highest.
  3. Oldest to newest.
  4. Newest to oldest (default).

5. Vote on Competition

Logged-in users can vote on the competition. You can either vote on one of the competitors. Once the vote is cast on one competitor, it cannot be removed. Also, you can cast your vote on only one of the competitors, not on both of them. It is also real-time, as soon as the vote is cast, it will automatically be displayed to all the users and the counter is incremented. The counter displays the number of votes cast on each competitor.

Votes

6. Delete Competition

Competitions can only be deleted by either of the users who created the competition, or by the admin. Once the competition is deleted, all the uploaded images will be deleted too. As soon as the competition is deleted, it will automatically be removed from all the other users too, without having them refresh the page.

7. Realtime Update using Sockets

Sockets are used for real-time communication. Instead of fetching the data from the server after regular intervals, sockets attach listeners to the client-side. Listeners are listening to the events sent from the server. The server will emit the event and the client will listen to that event and respond accordingly. In this project, sockets are used for the following features:

  1. When competition is created.
  2. When competition is deleted.
  3. To increase the counter after vote is casted to the competition.
  4. Notifications.

8. Notifications

When a competition is deleted by the admin, the admin will write the reason for the deletion. Thus, a notification will be sent to the user along with the reason why his competition was removed. By default, notification status is “unread” and they are highlighted. As soon as the user clicks on any of the notifications, that notification will be marked as “read” and it will no longer be highlighted.

Notifications

9. Load More Button

When the data in the database increases, it is not feasible to load all the data in a single AJAX request. So a “load more” button is created to solve this problem. For example, 10 records are fetched in the first AJAX request. The next 10 records will be fetched when the “load more” button is clicked, and so on.

Load More

10. Admin Panel

Admin panel is created so you (administrator) can delete any competition you find offensive. The default email and password of admin are:

email = admin@gmail.com
password = admin

11. Manage Competitions

Admin can delete competitions that he finds offensive. However, the admin must give the reason why that competition is deleted. A notification will be sent to the user who created that competition and he will be able to view it from the top navigation bar.

12. Reset Password

Now you will be able to reset your password if you ever forgot. You just need to enter your email address and an email will be sent to you with a link to reset the password. We are using the nodemailer module to send an email.

Forgot Password
Reset Password

13. Email Verification

When a new user registers, we are sending a verification email to the user’s entered email address. The user will not be able to log in until he verifies his email address. When a user clicks the link on his email address, he will receive a message that says that his account is verified. Then he will be able to log in successfully.

Email Verification

14. SMTP Configurations from Admin Panel

To send an email, you will need an SMTP server. Every SMTP server requires some configurations to set up that include, host, port, email, and password. You can write these values directly hardcoded in your code, but to update these values in the future, you have to find these values in the code and update them.

In this project, you can set these configurations directly from the admin panel. Once the values are set, new emails will be sent using the new configurations.

SMTP configurations from admin panel

15. Adult Image Validation

This is a must-have feature if you are creating a website that allows users to upload pictures and they will be seen to the world. Anyone can upload an image that contains adult content, and it will not be good for your business. So when the user is uploading pictures while creating competition, the system will automatically check if the image is safe to upload.

If the image is an adult image, then an error will be shown to the user and it will not be uploaded.

16. Admin Panel Stats

Admin can see total users, total competitions, and total votes cast so far. They are also real-time, so when a new user is registered, or new competition is created, or event a new vote is cast, it will automatically be incremented here.

Also, when competition is deleted, the number will automatically get decremented as well, without having the admin refresh the page.

Admin Panel Stats

17. Real-time Comments

Users can comment on each competition. And they are also real-time as well. Once a new comment is added, it will immediately be displayed to all the other users as well. They do not have to refresh the page to see new comments.

Real-time Comments

18. User Profile

Users can now update their names and profile pictures. We are using the fs (file system) module to upload the picture. User can also add their bio, date of birth, country, and social media links. Media links include Facebook, Instagram, google plus, Twitter, and LinkedIn.

User can also change their account password. In order to change the password, the user must enter the current password. The new password should be entered twice for confirmation.

User Profile

19. Free Customer Support

This is not a feature of the project, but it is a free service provided for the pro version only. That means if you find any difficulty in installing or configuring the project, we will help you install it. Also, if you encounter any error or a bug in the released version, then it can be fixed too.

These are all the features we have right now in the picture competition web app. We are open to more ideas. If you have more ideas to add, kindly do let us know.

Load more Node JS button with Mongo DB

In this tutorial, we are going to teach you how you can create a load more Node JS button with MongoDB in your web project. “Load more” means that we will be displaying a few records, let’s say 5 when the page loads. Then we will display a button that when clicked will fetch the next 5 records. It is similar to the Facebook infinite scroll, where when you reach the end of the page, it automatically fetched the next posts. If you prefer a video tutorial over a textual tutorial, you can find it below:

Setup the server

Make sure you have installed Node JS and Mongo DB in your system. To view the data in Mongo DB, you will need a software called Mongo DB Compass. That can be installed during Mongo DB installation. Using Mongo DB Compass, create a database named “loadmore_nodejs_mongodb”. Inside this database, you need to create a collection named “users”. Inside this collection, you can add 10 records for testing. I have added the name and age of each user, the ID is automatically generated by Mongo DB.

Then you need to create an empty folder anywhere on your computer. And create a new file inside it named “server.js”. At the same time, open your command prompt or Terminal in this folder. You can run the following command to enter this folder:

cd "path of folder"

While being inside the folder, run the following command to initialize it as an NPM:

npm init

After that, you need to install some modules using the following command:

npm install express http mongodb ejs express-formidable
  • We will be using an express framework.
  • HTTP module will be used to start the server at a specific port.
  • Mongo DB module will be used to connect and interact with the Mongo DB database.
  • EJS will be used to render HTML files.
  • Express formidable will be used to receive HTTP POST values, which we will be sending from AJAX.

If you do not have a nodemon module you can install it with this command:

npm install -g nodemon

Nodemon command helps you to automatically restart the server if there is any change in the server.js file. Now in server.js, initialize express framework. Then create an HTTP server object and start the server at port 3000.

// initialize express framework
var express = require("express");
var app = express();
 
// create http server
var http = require("http").createServer(app);

// start the server
http.listen(3000, function () {
    console.log("Server started at port 3000");
});

Run nodemon command to start the server.

nodemon server.js

Connecting Node JS with Mongo DB

Now we need to connect our Node JS Express app with Mongo DB. We have already installed the Mongo DB module. Now we need to include the Mongo DB and Mongo Client objects. Place the following lines in your server.js file before calling the HTTP listen function:

// include mongo DB module
var mongodb = require("mongodb");
var mongoClient = mongodb.MongoClient;

Now after line 10 from previous code, place the following lines to connect with database:

// connect with mongo DB server and database
mongoClient.connect("mongodb://localhost:27017", {
    useUnifiedTopology: true
}, function (error, client) {
    var database = client.db("loadmore_nodejs_mongodb");
    console.log("Database connected.");
});

Display HTML file

Now we need to show an empty table where data will be populated dynamically. We will be using EJS to render the HTML file. First, we need to tell our Express app that we will be using the EJS engine. So add the following lines before connecting the HTTP server in server.js:

// set the view engine as EJS for displaying HTML files
app.set("view engine", "ejs");

Now we need to create a GET route that when accessed from the browser will display an HTML page. Place the following lines after line 6 from the previous code:

// create a GET HTTP route to show page
app.get("/", function (request, result) {
    // render an HTML page
    result.render("index");
});

Now we need to create a folder named “views” and inside this folder, create a file named “index.ejs”. Inside this file, we will create a table, and give an ID to the <tbody> because we will be displaying data dynamically. And also a load more button.

<!-- views/index.ejs -->

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Number</th>
            <th>Age</th>
        </tr>
    </thead>

    <tbody id="data"></tbody>
</table>

<button type="button" onclick="getData();">
    Load More
</button>

Now you can access it by entering the following URL in your browser:

http://localhost:3000/

Get data from Mongo DB

Now we need to fetch data from the database in which a way that it will get 2 records each time a button is clicked. But the first 2 records will be fetched when the page loads. So create a script tag and create a variable that will be incremented each time that button is pressed. This variable will be used to skip the records to fetch the next ones.

<script>

    // Starting position to get new records
    var startFrom = 0;

    window.addEventListener("load", function () {
        // Calling the function on page load
        getData();
    });

</script>

Now we need to create that getData() function in index.ejs file. This will send an AJAX request to the route “load-more” with a method POST. We will also attach our startFrom variable with the AJAX object (line 45). When the response is successfully received from the server (line 15), it will be in a JSON string. So we convert that into Javascript arrays and objects (line 20). Then we are appending the newly fetched data in our tbody tag. And finally, we are incrementing the startFrom variable with the number of records we want to show in each AJAX call.

// This function will be called every time a button pressed 
function getData() {
    // Creating a built-in AJAX object
    var ajax = new XMLHttpRequest();

    // tell the method and URL of request
    ajax.open("POST", "http://localhost:3000/load-more", true);

    // Detecting request state change
    ajax.onreadystatechange = function () {

        // Called when the response is successfully received
        if (this.readyState == 4) {

            if (this.status == 200) {
                // For debugging purpose only
                // console.log(this.responseText);

                // Converting JSON string to Javasript array
                var data = JSON.parse(this.responseText);
                var html = "";
         
                // Appending all returned data in a variable called html
                for (var a = 0; a < data.length; a++) {
                    html += "<tr>";
                        html += "<td>" + data[a]._id + "</td>";
                        html += "<td>" + data[a].name + "</td>";
                        html += "<td>" + data[a].age + "</td>";
                    html += "</tr>";
                }
         
                // Appending the data below old data in <tbody> tag
                document.getElementById("data").innerHTML += html;

                // number of records you want to show per page
                var limit = 2;
         
                // Incrementing the offset so you can get next records when that button is clicked
                startFrom = startFrom + limit;
            }
        }
    };

    var formData = new FormData();
    formData.append("startFrom", startFrom);

    // Actually sending the request
    ajax.send(formData);
}

Now we need to create a POST route in our server.js. Before creating a POST route, we need to tell our Express app that we will be using the Express-Formidable module to get values (e.g. startFrom) from AJAX requests. So paste the following lines before starting HTTP server:

// server.js

// used to get POST field values
var formidable = require("express-formidable");
app.use(formidable());

Now write the following lines after the Mongo DB is connected:

// create a POST HTTP route to get data
app.post("/load-more", async function (request, result) {
 
    // number of records you want to show per page
    var limit = 2;
 
    // get records to skip
    var startFrom = parseInt(request.fields.startFrom);
 
    // get data from mongo DB
    var users = await database.collection("users").find({})
        .sort({ "id": -1 })
        .skip(startFrom)
        .limit(limit)
        .toArray();
 
    // send the response back as JSON
    result.json(users);
});

If you run the code now, you will see 2 records when the page loads. On clicking the load more button, 2 more records will be fetched, and so on.

Styling the table – CSS

At this point, you have successfully created a feature where you can display a few records when the page loads. And further records when user clicks a “load more” button. But we can give a few styles just to make it look good. The following styles will:

  • Give 1-pixel black border to the table and all its headings and data tags.
  • border-collapse will merge all the borders into one.
  • We are giving padding (distance between border and content) to all headings and data tags.
  • Give some margin from the bottom to the table to have some space between the table and load more button.
table, th, td {
    border: 1px solid black;
    border-collapse: collapse;
}
th, td {
    padding: 25px;
}
table {
    margin-bottom: 20px;
}

Now that our load more Node JS button is fully functional. We can also try another approach that is usually called “Infinite scroll”.

Infinite scroll

You can also use an “infinite scroll” like Facebook. Instead of displaying a button to load more data, more data will be fetched when the user scrolled to the bottom. No need to change the Node JS code that we used to load more button. For that, first you need to include jQuery.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

Then you can replace your window onload event listener with the following:

var loadMore = true;

window.addEventListener("load", function () {
    // Calling the function on page load
    getData();

    $(window).bind('scroll', function() {
        if(!loadMore && $(window).scrollTop() >= $('#data').offset().top + $('#data').outerHeight() - window.innerHeight) {
            // console.log('end reached');
            loadMore = true;
            getData();
        }
    });
});

And finally in your ajax.onreadystatechange listener, when you successfully append the data in the <tbody> tag, keep the infinite scroll working if there are more records.

if (data.length > 0) {
    loadMore = false;
}

So that’s how you can create a load more Node JS button with Mongo DB.

[wpdm_package id=’1251′]