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.

Pagination on arrays – MongoDB

In this tutorial, we will teach you, how you can do pagination on arrays of documents in MongoDB.

If you have a very large database, fetching all the documents in one query might be very slow. But we have a solution: Pagination. It allows you to fetch a few records in one query and the next few in another query. For example, if you have 1000 users, the first query will fetch users from 0 to 100, the second query will fetch users from 101 to 200, and so on.

Problem

Pagination comes with a problem; when data is stored in arrays. In Mongo DB, the data is stored in JSON format. You might be saving data in arrays. For example, take the following document:

db.collection("users").insertOne({
	name: "Adnan",
	workouts: [{
		name: "Push ups",
		sets: 10,
		reps: 30
	}, {
		name: "Pull ups",
		sets: 7,
		reps: 10
	}, {
		name: "Crunches",
		sets: 5,
		reps: 50
	}, {
		name: "Deadlifts",
		sets: 3,
		reps: 15
	}, {
		name: "Shoulder press",
		sets: 8,
		reps: 8
	}]
})

Here, one document has an array of “workouts” and it might have a lot more data in it. For example, the user we created above has 5 “workouts” array elements, but you want to show 2 workouts on each query. If you run the regular query to fetch the users document, then it will return all the 5 workouts:

// not a solution

// return the whole object
const data = await db.collection("users").find({
	name: "Adnan"
}).toArray()
console.log(data[0])

But it will return the whole object with all 5 “workouts” elements, which is not the desired outcome.

So how will you paginate on that ?

Solution

This is where the Mongo DB $slice operator comes in. It works the same as the Javascript slice() function. It cuts the array from provided start and end values. So we will use the following query to get the users document but with a few workouts:

// solution

// return the first 2 workouts
const data = await db.collection("users").find({
	name: "Adnan"
}, {
	projection: {
		workouts: {
			$slice: [0, 2]
		}
	}
}).toArray()
console.log(data[0])

Slice start value works on indexes, so 0 means the first element of the array, and the end means the number of elements to fetch. So it will fetch 2 array elements starting at index 0.

The second parameter to the find() function tells the projection, which means which keys you want to fetch. You have to write all your key names inside the “projection” object. Now if you want to fetch the next 2 records, you can simply do the following:

// return the workouts from 2 to 4 
const data = await db.collection("users").find({
	name: "Adnan"
}, {
	projection: {
		workouts: {
			$slice: [2, 2]
		}
	}
}).toArray()
console.log(data[0])

The “start” is set to 2, which means the array starts from index 2 which will be the 3rd element of the array. Because the first and second are already fetched in the previous query. The “end” is again set to 2 so we are again fetching the 2 records from the array.

That’s how you can keep going on and implementing pagination on your document’s arrays in MongoDB. If you face any problem in following this, kindly do let me know.

[wpdm_package id=’2045′]

A guide to MEVN

MEVN stands for Mongo DB, Express JS, Vue JS, and Node JS. It is a technology stack used by developers to create full-fledged web applications. Most of the applications created in the MEVN stack are single-page applications. But it is up to you if you want to single page or multi-page web application.

Table of content

  1. What is MEVN ?
  2. Why MEVN ?
    • Same language for frontend and backend
    • Easy deployment
    • Seamless user experience
    • APIs for mobile apps
    • Mongo DB is scaled horizontally
    • Short learning curve
    • Better support for sockets
  3. Future of MEVN
  4. Projects developed in MEVN
  5. Where to learn MEVN ?
    • Official documentation
    • YouTube
    • Blogs
  6. Issues you will face in the development

Let’s get started.

1. What is MEVN ?

MEVN is a technology stack used to develop full-fledged web applications. You can easily create scalable web applications either single-page or multi-page applications. It is a new yet quickly evolving technology stack that has been adopted by a lot of developers already. Learning this will not only make you a frontend developer but a backend developer as well. Thus increasing your demand and reputation in the community.

Hiring a developer in this stack is easy for companies as well. Hiring individual developers for the backend and frontend is costly, especially for startups. So they end up looking for someone who knows both the backend and frontend.

2. Why MEVN ?

If you learn this technology stack, your demand is automatically increased since companies would love to have a developer that can handle both frontend and backend tasks. Even if you didn’t get a job as a full-stack developer, you can still get a job as either a frontend or backend developer since you can work in both.

Same language for frontend and backend

In this stack, the primary language is Javascript. Node JS is a Javascript runtime environment, Vue JS is a Javascript frontend framework and Express JS is a framework of Node JS for creating APIs. Mongo DB syntax is also very similar to Javascript. So you do not need to learn Javascript for the frontend and PHP or Python for the backend. One language i.e. Javascript is used for both sides (frontend and backend).

Easy deployment

Vue JS builds are minified code of HTML, CSS, and Javascript. So they can easily be deployed on most servers. You do not need to worry if your hosting supports PHP, Java, Python, or any other language. If your hosting supports HTML (which almost every hosting does) then you are good to go.

Node JS can also be easily deployed on Virtual Private Servers (VPS) or dedicated servers. You can check our detailed guide on how to deploy Node JS on VPS or dedicated servers.

Mongo DB can also be deployed on VPS or dedicated servers. But it can also be deployed on Mongo DB’s official site. You can check our tutorial for Mongo DB deployment.

Seamless user experience

Most of the apps created in this stack are single-page applications. They provide a better user experience since there is no page reload on any link. Only the required section of the page changes dynamically. Some of the websites that are single-paged are Gmail, Facebook, and Twitter.

APIs for mobile apps

Most of the rendering is done on the client side. This means you have to create APIs for each request. This helps you in the future when you want to develop mobile apps for your website. Android and iOS apps can easily communicate with APIs developed in Node JS.

Mongo DB is scaled horizontally

Horizontal scaling is performed by adding more nodes to the servers. Horizontal scaling is best for a non-relational database like Mongo DB since all the data is stored in a single document.

Moreover, if you have to add one more attribute to a single entity, you can easily add it to that document only. Unless in MySQL, where either you have to create a column that will result in assigning the value to all rows. Or you have to create a separate table and join them in each query which will slow down your application.

In Mongo DB, all data required for an entity is stored in a single document. No relationships are made. So data can be saved on different servers without being dependent on each other.

Short learning curve

If you know Javascript, you already know Vue JS. Unlike in React where you have to use JSX syntax, in Vue JS you can use legacy HTML and CSS code and use Javascript with it. If you know the basics of Javascript, spend a few hours on Vue JS official documentation and you will become a Vue JS developer.

Better support for sockets

Sockets are used for real-time communication. Like on Facebook, you do not have to refresh the page to check new notifications. Whenever a new notification is received, it automatically displays an alert. If you develop your web application in the MEVN stack, you will be able to add real-time features like alerts, notifications, chat messages, real-time collaborations, etc.

3. Future of MEVN

MEVN proving itself a bright future. A lot of developers are moving to this stack. It is in the race with MEAN and MERN stacks. It has solved a lot of issues that developers faced in web development. For example, during infinite scroll, the web page hangs due to too much data. Vue JS solved this by using the useVirtualList component. Node JS has solved real-time communication problems via sockets. And Mongo DB has solved the scalability problems and also the problems that came with tightly-coupled databases.

4. Projects developed in MEVN

To show you how easy it is to develop websites in this technology stack, we developed some projects in it. We have developed a chat app, a customer support ticketing system, and more.

5. Where to learn MEVN

You can learn this technology stack from many sources.

Official documentation

To stay updated with the latest versions and features, check out the official documentation of Vue JS. Node JS can also be learned from their official documentation. Mongo DB also came with outstanding documentation.

YouTube

Check this playlist to find a lot of tutorials on the MEVN stack.

Blogs

We have created many text-based tutorials on this stack.

6. Issues you will face in the development

You need to spend a lot of time to make the application up and running. The first version of your product might take more time and effort because you need to set up the APIs and basic functionality. However, we have created a boilerplate for authentication that will save you 100% time on rewriting the authentication code.

How to pick random document from Mongo DB

We need to use the aggregate function to pick a random document from Mongo DB. Its usage is simple. It accepts an array of objects. You need to pass $sample object in one array object and tell the number of random documents you want to fetch in the size object. Suppose you have the following data in your users collection.

db.users.insertOne({ name: "Adnan", language: "Java" })
db.users.insertOne({ name: "Tech", language: "Node JS" })
db.users.insertOne({ name: "Developer", language: "Java" })
db.users.insertOne({ name: "Programmer", language: "C++" })

Then the Mongo DB query to return 1 random document would be:

db.users.aggregate([
	{
		$sample: {
			size: 1
		}
	}
])

This will return 1 random document from your users collection. If you want to apply some query to it, you can do it by passing another array element and this time using the $match operator.

db.users.aggregate([
	{
		$match: {
			language: "Java"
		}
	},
	
	{
		$sample: {
			size: 1
		}
	}
])

This will return 1 random document from users collection whose language is “Java”. Make sure the $match operator is the first element of the array, otherwise it will return empty records sometimes.

Video tutorial

You can learn more about Mongo DB aggregate function from their official documentation. Check out our tutorials on Mongo DB.

Login with JWT – Kotlin, Node JS, Mongo DB

In this tutorial, we will teach you how you can login a user with JWT (JSON Web Token) in the android app using Kotlin and Node JS. We will be using Node JS to generate JWT and Mongo DB to save it.

Creating an activity

First, we will create an empty activity named “LoginActivity.kt”. Following will be the code of that file:

package com.adnantech.myapp

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class LoginActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
    }
}

This will look for a file named “activity_login.xml” inside the “res/layout” folder.

Create an XML file

Then we will create a file named “activity_login.xml” inside the “res/layout” folder. That file will have the following code:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="20dp"
    tools:context=".LoginActivity">

    <TextView
        android:id="@+id/heading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Login"
        android:textAlignment="center"
        android:textSize="40sp"
        android:textStyle="bold" />

    <EditText
        android:id="@+id/etPhone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/heading"
        android:layout_marginTop="20dp"
        android:hint="Enter phone"
        android:inputType="phone" />

    <EditText
        android:id="@+id/etPassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etPhone"
        android:layout_marginTop="20dp"
        android:hint="Enter password"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/btnLogin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etPassword"
        android:layout_marginTop="20dp"
        android:text="Login" />

</RelativeLayout>

It will display the heading “Login” horizontally center-aligned. Two input fields. One to enter the phone number, and one for the password. It will also display a button that when clicked will call an HTTP request to authenticate the user.

Button onclick event listener

We need to attach an “onclick” event listener to the button. So we will create instances of all our input fields and button.

class LoginActivity : AppCompatActivity() {

    private lateinit var etPhone: EditText
    private lateinit var etPassword: EditText
    private lateinit var btnLogin: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        etPhone = findViewById(R.id.etPhone)
        etPassword = findViewById(R.id.etPassword)
        btnLogin = findViewById(R.id.btnLogin)

        btnLogin.setOnClickListener {
            doLogin()
        }
    }

    private fun doLogin() {
        // [HTTP request to authenticate the user]
    }
}

And inside this button click listener, we are calling our method that will call an HTTP request.

Calling an HTTP request

To call an HTTP request, first, make sure you have INTERNET and ACCESS_NETWORK_STATE permissions added in your “AndroidManifest.xml” file.

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

Then you need to add the following line in your “app/build.gradle” file:

implementation "com.android.volley:volley:1.2.1"

Make sure to “Gradle sync” after pasting that line. You will see the option to sync Gradle on top in Android studio. After that, you need to write the following code in the [HTTP request to authenticate the user] section:

btnLogin.isEnabled = false

val queue = Volley.newRequestQueue(this)
val url = "http://172.20.10.4:3000/login"

val requestBody =
    "phone=" + URLEncoder.encode(
        etPhone.text.toString(),
        "UTF-8"
    ) + "&password=" + etPassword.text
val stringReq: StringRequest =
    object : StringRequest(
        Method.POST, url,
        Response.Listener { response ->
            btnLogin.isEnabled = true
            Log.i("mylog", response)
        },
        Response.ErrorListener { error ->
            Log.i("myLog", "error = " + error)
        }
    ) {
        override fun getBody(): ByteArray {
            return requestBody.toByteArray(Charset.defaultCharset())
        }
    }
queue.add(stringReq)

This will first disable the login button. Then it will create an instance of Volley. After that, it sets the path of an API. You can get your IP address by running either of the following commands in your terminal:

> ipconfig /all
or
> ifconfig

You need to copy the ipv4 address and paste it as your IP address.

Then it creates a request body, the parameters that will be sent to the API. Creates an instance of “StringRequest”. When the response is received, it simply enables the login button and displays the response in the log. You can view the response by opening your “logcat” in your android studio bottom bar. And select “info” and search for “mylog”.

If you want to convert the JSON string into Kotlin class, you can check out this tutorial.

We have already created a public GitHub repo for the authentication API. You can check it out here.

So that’s how you can create a login activity in android using Kotlin with Node JS and Mongo DB as the backend. If you face any problem in following this, kindly do let me know.

User registration – Kotlin, Node JS, Mongo DB

This tutorial will create an android user registration activity in Kotlin. You will learn the following:

  • How to use Node JS API for android apps
  • How to call HTTP request with POST method in Kotlin

Creating an activity

First, we will create an empty activity named “RegisterActivity.kt”. Following will be the code of that file:

package com.adnantech.myapp

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class RegisterActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_register)
    }
}

This will look for a file named “activity_register.xml” inside the “res/layout” folder.

Create an XML file

Then we will create a file named “activity_register.xml” inside the “res/layout” folder. That file will have the following code:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="20dp">

    <TextView
        android:id="@+id/heading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Register"
        android:textAlignment="center"
        android:textSize="40sp"
        android:textStyle="bold" />

    <EditText
        android:id="@+id/etName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/heading"
        android:layout_marginTop="20dp"
        android:hint="Enter name" />

    <EditText
        android:id="@+id/etPhone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etName"
        android:layout_marginTop="20dp"
        android:hint="Enter phone"
        android:inputType="phone" />

    <EditText
        android:id="@+id/etPassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etPhone"
        android:layout_marginTop="20dp"
        android:hint="Enter password"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/btnRegister"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etPassword"
        android:layout_marginTop="20dp"
        android:text="Register" />

</RelativeLayout>

It will display the heading “Register” horizontally center-aligned. Three input fields. One to enter the name, one for the phone number, and one for the password. It will also display a button that when clicked will call an HTTP request to save that data.

Button onclick event listener

We need to attach an “onclick” event listener to the button. So we will create instances of all our input fields and button.

class RegisterActivity : AppCompatActivity() {

    private lateinit var etName: EditText
    private lateinit var etPhone: EditText
    private lateinit var etPassword: EditText
    private lateinit var btnRegister: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_register)

        etName = findViewById(R.id.etName)
        etPhone = findViewById(R.id.etPhone)
        etPassword = findViewById(R.id.etPassword)
        btnRegister = findViewById(R.id.btnRegister)

        btnRegister.setOnClickListener {
            doRegister()
        }
    }

    private fun doRegister() {
        // [HTTP request code goes here]
    }
}

And inside this button click listener, we are calling our method that will call an HTTP request.

Calling an HTTP request

To call an HTTP request, first, make sure you have INTERNET and ACCESS_NETWORK_STATE permissions added in your “AndroidManifest.xml” file.

&lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
&lt;uses-permission android:name="android.permission.INTERNET" />

Then you need to add the following line in your “app/build.gradle” file:

implementation "com.android.volley:volley:1.2.1"

Make sure to “Gradle sync” after pasting that line. You will see the option to sync Gradle on top in Android studio. After that, you need to write the following code in the [HTTP request code goes here] section:

btnRegister.isEnabled = false

val queue = Volley.newRequestQueue(this)
val url = "http://172.20.10.4:3000/register"

val requestBody =
    "name=" + etName.text + "&phone=" + URLEncoder.encode(etPhone.text.toString(), "UTF-8") + "&password=" + etPassword.text
val stringReq: StringRequest =
    object : StringRequest(Method.POST, url,
        Response.Listener { response ->
            btnRegister.isEnabled = true
            Log.i("mylog", response)
        },
        Response.ErrorListener { error ->
            Log.i("myLog", "error = " + error)
        }
    ) {
        override fun getBody(): ByteArray {
            return requestBody.toByteArray(Charset.defaultCharset())
        }
    }
queue.add(stringReq)

This will first disable the registration button. Then it will create an instance of Volley. After that, it sets the path of an API. You can get your IP address by running either of the following commands in your terminal:

> ipconfig /all
or
> ifconfig

You need to copy the ipv4 address and paste it as your IP address.

Then it creates a request body, the parameters that will be sent to the API. Creates an instance of “StringRequest”. When the response is received, it simply enables the registration button and displays the response in the log. You can view the response by opening your “logcat” in your android studio bottom bar. And select “info” and search for “mylog”.

Converting JSON string response to class

If your API is returning a JSON string in response, then you also need to convert that JSON string response to Kotlin class. You can do that by installing a library called “Gson”. You can easily install it by adding the following line in “app/build.gradle” and running “Gradle sync”:

implementation "com.google.code.gson:gson:2.8.5"

Then create a package named “models” since it is a good practice to keep all your response classes in a separate package. Inside this package, create a file named “GeneralResponse.kt” and write the following code in it:

package com.adnantech.myapp.models

class GeneralResponse {
    lateinit var status: String
    lateinit var message: String
}

When the response is received you simply need to do the following with the “response” string:

val generalResponse: GeneralResponse = Gson().fromJson(response, GeneralResponse::class.java)

This will convert the JSON string into your class object.

Creating an API in Node JS

We already have created a public GitHub repo that will give you a pre-built registration API. You can make the changes as per your needs. Just download the repo. Paste it anywhere on your computer. Run the following commands in the “api” folder:

> npm update
> node server.js

Make sure you have Node JS installed in your system. That’s how you can create a user registration activity in Kotlin with Node JS and Mongo DB as the backend. Check out our tutorials on Android to learn more.

Android chat app in Kotlin and Node JS

We created a chat application in native Android using Kotlin and Node JS. We have used Mongo DB as a database.

Modern and professional UI

End-to-end encryption

Usually, we use encryption but on server side. It has a security issue i.e. plain messages are transmitted through a network that can be read or altered in-transit.

But in this app, we have implemented end-to-end encryption means the encryption and decryption is done on client side rather than on server side. Client will encrypt the message and send it to the server. Server will send the encrypted message to the receiver and the receiver client will do the decryption. So even the server will not be able to read your messages.

We will be using the AES-256-bit length key to generate secret keys. All messages are sent to the server after encryption. So data will remain safe in-transit.

Voice notes

Sending voice notes is a quick way to send messages especially when you are in places when you are in hurry. Instead of typing the message which might cause spelling mistake, you can simply record your audio and send it directly to the receiver inside the app. No need to record the audio from a separate app and attach the audio in a message. You can also listen to your sent or received voice notes within the app.

If you use WhatsApp, you already know how voice notes work. You will learn how to record audio from your android phone and save it in your phone’s storage. Once stored, we will send that MP3 file to the Node JS server. The server will save the file. The user will be able to play that audio from the URL.

All contacts

We are using android contact’s API to fetch all the contacts. So you will learn how to get runtime contacts permission in android.

Private chat

The user can chat with any of his contact numbers as long as the receiver is a registered user.

Chat attachments

Users can attach files in chat messages as well. Images and videos are not compressed at all. So the receiver will see the image in its original quality.

Search contacts

Group chat

Users can chat in groups. Create as many groups as you want. Add members to them and start chatting. What makes it different from other chat apps ? First, it does not allow anyone to add you to a group. You will be invited. You will only start receiving the group notifications when you accept the invitation. Second, this app displays a list of all groups separately. So you can know in which groups you are currently in.

Share status that disappears after 24 hours

In this tutorial, you will:

  • Be able to share your status with all your contacts
  • Exclude some contacts from viewing your status
  • Create multiple lists (e.g. friends, family, colleagues, etc.) so you won’t have to select the contacts each time you upload a status
  • See contacts who have viewed your status/story
  • Download the image or video of status

Seen/unseen messages (blue ticks)

In this part, you will learn how to:

  • Install sockets in android
  • Connect android sockets with the Node JS server
  • Get real-time events on new message
  • Mark messages as read and un-read
  • Get bluetick (like WhatsApp) when the user sees your message

User profile

Search messages

Learn how to apply case in-sensitive sub-string search on all the messages in a chat with any of your contact. You can write any part of text of message and the app will show you the messages that matched your searched string.

Although the messages are end-to-end encrypted, which means that the server will not be able to apply search because server can’t read your messages. But we still found a way to apply search on end-to-end encrypted messages.

How to install

Our TrustPilot Reviews

TrustPilot-reviews

Ecommerce website in MEVN stack

It is a single-page ecommerce website developed in Vue JS. Vue JS is an open-source Javascript framework specifically built to design user interfaces and is widely used for creating single-page applications.

Node JS is a Javascript run-time environment developed for creating the backend of applications. Its framework Express is widely used for creating APIs. It is built to create scalable applications.

Mongo DB is a no-SQL database. It is a non-relational database system. It is easily scalable because the data is loosely coupled.

FeaturesFreePremium $100
Product management (admin panel)YesYes
Product listingYesYes
Product detailYesYes
Shopping cartYesYes
Checkout (PayPal & Stripe)YesYes
Order management (admin panel)YesYes
Order detailYesYes
Product specificationsYesYes
Stock managementYesYes
Search and sortYesYes
New order emailYesYes
Product reviewsNoYes
Shipping charges by countryNoYes
Product image compressionNoYes
Realtime chat between users and adminNoYes

Following is the feature list of this project:

  • Product management (admin panel)
  • Product listing
  • Product detail
  • Shopping cart
  • Checkout (PayPal & Stripe)
  • Order management (admin panel)
  • Order detail
  • Product specifications
  • Stock management
  • Product reviews
  • Search and sort
  • Shipping charges by country
  • Email to admin whenever a new order is placed
  • Product image compression
  • Realtime chat between users and admin

Add product

Admin will be able to add products. He will enter the name of the product. Add a little description of the product. And set the price of the product. The price will be in dollars. Along with them, the admin can upload one or more images of the product.

Product listing

Users will see the products added by the admin on their home page. The latest products are displayed first. Along with each product, a button will be displayed to view the detail of the product.

Product detail

Users can view the detail of the product. If the product contains more than one image, then it will be displayed as a beautiful slider. From this page, users will also be able to add products to the cart. If the product is already added to the cart, then he will be able to remove it from the cart.

Shopping cart

Users can add the product to the cart. From the cart’s page, he will be able to adjust the quantity of the product. There will be multiple products in the cart and you can set the quantity of each product separately. You will also see your total bill as soon as you change the quantity of any product added to the cart.

Checkout (Stripe & PayPal)

After adding the products to the cart and setting each product’s quantity, the user can go to the checkout page. Here, he can either pay via Stripe or PayPal. Both payment methods accept debit and master cards securely. When the user made the payment, his payment is verified from the server. If verified, then a new order will be created in the database.

Order management

Whenever a user made a payment via Stripe or PayPal, an order will be created. All orders will be displayed on the admin panel ordered by latest to oldest. By default, the status of the order is “Processing”. But admin can change the status of the order to “Completed” when the product is delivered to the customer.

Admin will also be able to view the total amount of the order, the payment method customer has used, and the user’s shipping details.

Order detail

Admin can view all the products that were included in each order. He can manually verify the payment by clicking the payment method. If he clicks “Stripe”, he will be redirected to the Stripe payments page. If he clicks “PayPal”, then he will be redirected to the PayPal business page. Admin can check the name, email, and phone of the user and also his shipping address.

Product specifications

Admin can add the specifications of each product and the user will be able to see it on the product detail page.

Stock management

Admin can set the number of units in stock for each product. When the user makes an order, then the number of units he has selected will be subtracted.

Product reviews

User can give reviews on a product and it will be displayed to all users who visit that product. Admin will have the ability to remove any review, for example, spamming, etc.

Search and sort

Users can search by product by its name, description, category, or specifications. Users can also sort the products by date or by price.

Shipping charges by country

Admin can set the shipping charges by each country because, in a global eCommerce website, users place orders from all over the world. For example, if your store is located in USA and someone places an order from the UK. Then you charge the shipping fee differently than other countries.

Email to admin whenever a new order is placed

Admin will receive an email whenever a new order has been placed by the user.

Product image compression

Now you can compress the product images from the admin panel. The 3 MB image can be reduced to just 182 KB and the image will still be of great quality.

Realtime chat between users and admin

Users will be able to have a chat with the admin to know more about a product before placing an order. I believe every eCommerce website should have this feature.

We are constantly updating this project and adding more features and enhancements to it. Following are all the builds that are released till today.

  • In the very first release, we build the basic E-commerce version where the admin can add products and users can see them. Add them to carts and order them. We also added Stripe and PayPal payment methods.
  • In the second release, we added more features. Allowing users to give reviews about a product. Allowing admin to charge a different shipping fee for each country and many more.
  • In the third release, we added functionality to compress product images and to have real-time chat between users and admin.

Free version

https://github.com/adnanafzal565/ecommerce-mevn-stack