Record and save audio from web – Javascript, Node JS, PHP

In this article, we are going to teach you, how you can record and save audio from web using Javascript. For saving audio files, we will teach you how you can do that with PHP and Node JS.

Let’s get started

First, we will create a folder named “record-audio-from-web”. Inside this folder, create a file named “index.html” and include fontawesome CDN in it. We will be displaying a microphone icon to start recording.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />

Then we will create a microphone icon.

  1. Give it a unique ID so we can get it in Javascript.
  2. Attach an “onclick” listener that will be called whenever that icon is clicked.
  3. And set the cursor to pointer so user will know that this is clickable.
<i class="fa fa-microphone" id="icon-record-audio" onclick="recordAudio()" style="cursor: pointer;"></i>

Now in Javascript, we first have to create an instance of recorder.

let recorder = null

Get microphone permission

Then we will create that function that will be called when the microphone icon is clicked. This function will:

  1. Get the permission to access laptop’s microphone.
  2. Initialize a media recorder object.
  3. Start recording audio from microphone.
// function to record audio
async function recordAudio() {
    // get permission to access microphone
	navigator.permissions.query({name: 'microphone'})
		.then(function (permissionObj) {
			console.log(permissionObj.state)				
		})
		.catch(function (error) {
			console.log('Got error :', error);
		})

    // get recorder object
	recorder = await doRecordAudio()

    // start audio
	recorder.start()
}

Record audio

Then we will create “doRecordAudio” function that will handle all the recording and stoping mechanism.

function doRecordAudio() {
    return new Promise(function (resolve) {
        // get user audio media
        navigator.mediaDevices.getUserMedia({
            audio: true
        })
        .then(function (stream) {
            // [media recorder goes here]
        })
    })
}

After that, we need to create a media recorder object. Following code goes in the [media recorder goes here] section:

// create media recorder object
const mediaRecorder = new MediaRecorder(stream)

// save audio chunks in an array
const audioChunks = []
mediaRecorder.addEventListener("dataavailable", function (event) {
    audioChunks.push(event.data)
})

// [start and stop listener goes here]

This will create a media recorder object from stream and save all audio chunks (when available) in an array.

Then write the following code in [start and stop listener goes here] section to create start and stop listeners:

// create a start function
const start = function () {
    // [start listener goes here]
}

// create a stop function
const stop = function () {
    return new Promise(function (resolve) {
        // [stop listener goes here]
    })
}

// send the values back to promise
resolve({
    start,
    stop
})

This “start” function gets called from “recordAudio()” function.

Start recording audio

When the start function is called, we need to:

  1. Change the microphone icon to stop icon.
  2. Change onclick listener, so next time the icon is clicked, we will stop the recording.
  3. And play the audio.
  4. Tell the media to start recording.

Following code goes in the [start listener goes here] section:

// when recording starts, set the icon to stop
document.getElementById("icon-record-audio").className = "fa fa-stop-circle"

// on icon clicked
document.getElementById("icon-record-audio").onclick = async function() {
    // stop the recorder
    if (recorder != null) {
        const audio = await recorder.stop()

        // [play audio]

        // [get audio base64 string]
    }
}

// start media recorder
mediaRecorder.start()

If you test now, you will be able to record the audio. The microphone icon will be changed as well. But now you need to find a way to stop the recording.

Stop the recording

When you are done recording the audio, you can click the icon again to stop it. Following code goes in the [stop listener goes here] section. This will:

  1. Attach a listener that will be called when the recording is stopped.
  2. Change the icon back to microphone.
  3. Reset the onclick listener to its initial state.
  4. Convert the audio chunks array into a blob.
  5. Create URL object from blob.
  6. Create an audio object to play when required.
  7. And stop the media recorder.
// on recording stop listener
mediaRecorder.addEventListener("stop", function () {

    // change the icon back to microphone
    document.getElementById("icon-record-audio").className = "fa fa-microphone"

    // reset the onclick listener so when again clicked, it will record a new audio
    document.getElementById("icon-record-audio").onclick = async function() {
        recordAudio()
    }

    // convert the audio chunks array into blob
    const audioBlob = new Blob(audioChunks)

    // create URL object from URL
    const audioUrl = URL.createObjectURL(audioBlob)

    // create an audio object to play
    const audio = new Audio(audioUrl)
    const play = function () {
        audio.play()
    }

    // send the values back to the promise
    resolve({
        audioBlob,
        play
    })
})

// stop the media recorder
mediaRecorder.stop()

If you test now, you can record the audio and stop the audio. The icon will change from microphone to stop and then from stop to microphone.

Play the audio after recording

In order to play the audio you just recorded, write the following line in the [play audio] section:

audio.play()

“stop” function will return the promise containing the “new Audio” object and calling “play()” will play the audio.

Converting audio to base64

We are already sending audioBlob from “stop” function, so we can create base64 string from it. Write the following code in your [get audio base64 string] section:

// get audio stream
const reader = new FileReader()
reader.readAsDataURL(audio.audioBlob)
reader.onloadend = function() {
    // get base64
    let base64 = reader.result

    // get only base64 data
    base64 = base64.split(',')[1]

    // send base64 to server to save
    sendVoiceNote(base64)
}

This will create a base64 string fom audio.

Sending audio base64 to server

Now we will create a function that will call an AJAX to save this base64 string as audio.

function sendVoiceNote(base64) {
        // create an instance for AJAX
        var ajax = new XMLHttpRequest()

        // set request method as POST, set URL and set asynchronous to true
        ajax.open("POST", "http://localhost:8888/tutorials/record-audio-from-web/sendVoiceNote.php", true)

        // send base64 string to server
        const formData = new FormData()
        formData.append("base64", base64)
        ajax.send(formData)
}

Note: In your server, create a folder named “audios” where all audio files will be saved.

Saving audio file on server – PHP

First, we will teach you how can you save it using PHP. Create a file named “sendVoiceNote.php” and write the following code in it:

<?php

$base64 = $_POST["base64"];

$file_name = "audios/" . time() . ".webm";
file_put_contents($file_name, base64_decode($base64));

echo $file_name;

Try recording an audio again. When you stop the audio, you will see that a new file will be saved in your “audios” folder. You can listen to your audio in your favorite media player (VLC etc).

Saving audio file on server – Node JS

Now we will teach you, how you can save the file using Node JS. Create a Node JS project by running the following commands one-by-one:

> npm install express http express-formidable fs cors
> npm install -g nodemon
> nodemon server.js

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

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

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

const cors = require("cors")
app.use(cors())

const fileSystem = require("fs")
const http = require("http").createServer(app)

http.listen(3000, function () {
	app.post("/sendVoiceNote", async function (request, result) {
		const base64 = request.fields.base64

        const buffer = Buffer.from(base64, "base64")
        const voiceNote = "audios/" + new Date().getTime() + ".webm"
        await fileSystem.writeFileSync(voiceNote, buffer)

        result.send(voiceNote)
	})
})

In your client side, change the AJAX “open” function call from:

ajax.open("POST", "http://localhost/record-audio-from-web/sendVoiceNote.php", true)

To:

ajax.open("POST", "http://localhost:3000/sendVoiceNote", true)

So that’s it. That’s how you can record audio from web and save it in your server using Node JS or PHP. If you face any problem in following this, kindly do let me know.

Here is the complete code of all files:

index.html

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<i class="fa fa-microphone" id="icon-record-audio" onclick="recordAudio()" style="cursor: pointer;"></i>

<script>

    let recorder = null

    function sendVoiceNote(base64) {
        // create an instance for AJAX
        var ajax = new XMLHttpRequest()

        // set request method as POST, set URL and set asynchronous to true
        // ajax.open("POST", "http://localhost:3000/sendVoiceNote", true)
        ajax.open("POST", "http://localhost/record-audio-from-web/sendVoiceNote.php", true)

        // whenever the status of request is changed
        // ajax.onreadystatechange = async function() {
        //     if (this.readyState == 4) {
        //         if (this.status == 200) {
        //             console.log(this.responseText)
        //         }

        //         if (this.status == 500) {
        //             console.log(this.responseText)
        //         }
        //     }
        // }

        // send base64 string to server
        const formData = new FormData()
        formData.append("base64", base64)
        ajax.send(formData)
    }

	function doRecordAudio() {
        return new Promise(function (resolve) {
            // get user audio media
            navigator.mediaDevices.getUserMedia({
                audio: true
            })
            .then(function (stream) {
                // create media recorder object
                const mediaRecorder = new MediaRecorder(stream)

                // save audio chunks in an array
                const audioChunks = []
                mediaRecorder.addEventListener("dataavailable", function (event) {
                    audioChunks.push(event.data)
                })

                // create a start function
                const start = function () {

                    // when recording starts, set the icon to stop
                    document.getElementById("icon-record-audio").className = "fa fa-stop-circle"

                    // on icon clicked
                    document.getElementById("icon-record-audio").onclick = async function() {
                        // stop the recorder
                        if (recorder != null) {
                            const audio = await recorder.stop()

                            // play the audio
                            audio.play()

                            // get audio stream
                            const reader = new FileReader()
                            reader.readAsDataURL(audio.audioBlob)
                            reader.onloadend = function() {
                                // get base64
                                let base64 = reader.result

                                // get only base64 data
                                base64 = base64.split(',')[1]

                                // send base64 to server to save
                                sendVoiceNote(base64)
                            }
                        }
                    }

                    // start media recorder
                    mediaRecorder.start()
                }

                // create a stop function
                const stop = function () {
                    return new Promise(function (resolve) {

                        // on recording stop listener
                        mediaRecorder.addEventListener("stop", function () {

                            // change the icon back to microphone
                            document.getElementById("icon-record-audio").className = "fa fa-microphone"

                            // reset the onclick listener so when again clicked, it will record a new audio
                            document.getElementById("icon-record-audio").onclick = async function() {
                                recordAudio()
                            }

                            // convert the audio chunks array into blob
                            const audioBlob = new Blob(audioChunks)

                            // create URL object from URL
                            const audioUrl = URL.createObjectURL(audioBlob)

                            // create an audio object to play
                            const audio = new Audio(audioUrl)
                            const play = function () {
                                audio.play()
                            }

                            // send the values back to the promise
                            resolve({
                                audioBlob,
                                play
                            })
                        })

                        // stop the media recorder
                        mediaRecorder.stop()
                    })
                }

                // send the values back to promise
                resolve({
                    start,
                    stop
                })
            })
        })
    }

    // function to record audio
	async function recordAudio() {
        // get permission to access microphone
		navigator.permissions.query({name: 'microphone'})
			.then(function (permissionObj) {
				console.log(permissionObj.state)				
			})
			.catch(function (error) {
				console.log('Got error :', error);
			})

        // get recorder object
		recorder = await doRecordAudio()

        // start audio
  		recorder.start()
	}
</script>

server.js

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

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

const cors = require("cors")
app.use(cors())

const fileSystem = require("fs")
const http = require("http").createServer(app)

http.listen(3000, function () {
	app.post("/sendVoiceNote", async function (request, result) {
		const base64 = request.fields.base64

        const buffer = Buffer.from(base64, "base64")
        const voiceNote = "audios/" + new Date().getTime() + ".webm"
        await fileSystem.writeFileSync(voiceNote, buffer)

        result.send(voiceNote)
	})
})

sendVoiceNote.php

<?php

$base64 = $_POST["base64"];

$file_name = "audios/" . time() . ".webm";
file_put_contents($file_name, base64_decode($base64));

echo $file_name;

How to call Node JS function from EJS

In this tutorial, you will learn how to call a function in your Node JS server file from the EJS template file.

First, we are going to install all the required modules. Run the following commands in your terminal:

> npm install http express ejs
> npm install -g nodemon
> nodemon server.js

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

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

const port = process.env.PORT || 3000
http.listen(port, function () {
    app.get("/", async function (request, result) {
        const html = await ejs.renderFile("views/index.ejs", null, {
            async: true
        })

        result.send(html)
        return
    })
})

Then, create a folder named views and inside this folder, create a file named index.ejs

To call a function from EJS file, we will first create a helper module in our server file. Create a file named helper.js at the root of your project. Following code will go in this file:

module.exports = {
	myFunc() {
		return "My function value."
	}
}

Then, include this module in your server.js file.

const helper = require("./helper")

And pass it in your renderFile function like this:

const html = await ejs.renderFile("views/index.ejs", {
    helper: helper
}, {
    async: true
})

Now you can easily call it from your index.ejs in the following way:

<%= helper.myFunc() %>

Since you are rendering the EJS file using await command, if you have to include another EJS file, you have to include it using await as well.

<%- await include ("header") %>

So that’s how you can call function in Node JS server file from your EJS template file. If you face any problem in following this, kindly do let me know.

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.

Video buffering and streaming in NodeJS

In this tutorial, we are going to teach you, how you can create video streaming and buffering like YouTube in NodeJS. In YouTube, you have noticed that the whole video is not downloaded in the browser at once. Only 10-15 seconds of the video has been downloaded in the browser. And more will be downloaded as user continues to watch.

Same technique it applied by Netflix where you watch just the 10-15 seconds of video and the next 10 seconds are fetched as you watch. This technique is useful in 2 terms:

  1. First, it provides a layer of security. You do not write the direct path of video in the <video> tag. You write the API route, and the API will fetch the video data.
  2. Second, it saves bandwidth. If a file is of 100 MB in size, user can download just 1 MB of at start. User can start watching the 1 MB and the next MBs will automatically gets downloaded periodically. User does not have to wait for whole 100 MBs to get downloaded.

Let’s get started

Start by creating an empty folder in your computer. Make sure you have NodeJS installed in your system. Open command prompt or terminal in that folder and run the following command in it:

> npm install express fs ejs

express module is used for routing. fs stands for File System, this module will be used to read the video file. ejs is a templating engine that will be used to render the HTML file. We will display the video in a <video> tag in an HTML file.

Once you ran the above commands, create a file named server.js. This is where we will do all the streaming and buffering work. Write the following code in that file:

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

app.set("view engine", "ejs")

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

app.listen(3000, function () {
  console.log("Server started.")
})

Save the file and run the URL: http://localhost:3000 in the browser. You might see an error that the file video.ejs in not found in the views folder. You need to create a folder named views and inside this folder, create a file named video.ejs.

Following will be the code of views/video.ejs file.

<video id="videoPlayer" width="650" controls>
	<source src="http://localhost:3000/video" type="video/mp4" />
</video>

You can see that we have not set the direct path of the video. Instead we wrote the path of our API endpoint that will return the video data.

Buffer the video

Now we need to create an API endpoint that will handle this request. Create the following GET route in your server.js file.

app.get("/video", function (req, res) {
  // Ensure there is a range given for the video
  const range = req.headers.range
  if (!range) {
    res.status(400).send("Requires Range header")
  }

  // get video stats (about 100MB)
  const videoPath = "video.mp4"
  const videoSize = fs.statSync(videoPath).size

  // Parse Range
  // Example: "bytes=32324-"
  const CHUNK_SIZE = 10 ** 6 // 1MB
  const start = Number(range.replace(/\D/g, ""))
  const end = Math.min(start + CHUNK_SIZE, videoSize - 1)

  // Create headers
  const contentLength = end - start + 1
  const headers = {
    "Content-Range": `bytes ${start}-${end}/${videoSize}`,
    "Accept-Ranges": "bytes",
    "Content-Length": contentLength,
    "Content-Type": "video/mp4",
  }

  // HTTP Status 206 for Partial Content
  res.writeHead(206, headers)

  // create video read stream for this particular chunk
  const videoStream = fs.createReadStream(videoPath, { start, end })

  // Stream the video chunk to the client
  videoStream.pipe(res)
})

** operator is used to raise the first operand to the power of second operand. That’s how you can create a video streaming and buffering engine in NodeJS. Comments has been added with each line for explanation. If you refresh the page now, you will start seeing the video with buffer on it. If you face any problem in following this, kindly do let me know.

Microservices in NodeJS

Microservices architecture in NodeJS is a group of loosely-coupled services that works independently of each other. Failure in one service does not affect the other services in the system. Consider each service as a separate server or system.

For example, you have a social networking website. So you can create one microservice that handles all functions of users, one microservice for managing posts, one microservice for comments etc.

Today, we are going to create such microservices in Node JS. First, we are going to create 2 microservices. One for handling users and one for posts. Then we will create a gateway that will route the API requests to the relevant service.

  • Directory structure:
    • users
      • server.js
    • posts
      • server.js
    • gateway
      • server.js

Create users microservice

First, we are going to create a microservice for users. Create a folder named users and inside it create a file named server.js. After that, open command prompt or terminal in this users folder and run the following commands one-by-one:

> npm init
> npm install -g nodemon
> npm install express
> nodemon server.js

Following will be the code of your server.js file inside users folder.

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

app.get("/getData", function (request, result) {
	result.send("Get data from users.")
})

app.listen(3001, function () {
	console.log("User service is running.")
})

It simply creates a service running at port 3001. This will be your separate server for handling users requests.

Create posts microservice

Now we will create a microservice for handling posts requests. Create a folder named posts and inside it create a file named server.js. After that, open command prompt or terminal in this posts folder and run the following commands one-by-one:

> npm init
> npm install express
> nodemon server.js

The commands are same as we did in users folder. Open the server.js file inside posts folder and write the following code in it.

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

app.get("/getData", function (request, result) {
	result.send("Get data from posts.")
})

app.listen(3002, function () {
	console.log("Post service is running.")
})

It is almost similar to the user’s server.js file. Only the port number is different. It is running on port 3002.

So you have 2 different services that runs independently. If user server fails, the posts server will keep on running independently. Now we need to have a gateway in between them, that will route the users API requests to user’s server and posts API requests to post’s server.

Note that each miroservice should run on a different server or process. Because the whole point of creating microservices is to create loosely-coupled services.

Create a gateway

All the API requests will be sent to the gateway server. The gateway server will then redirect the request to the relevant service.

Create a folder named gateway. Create a file named server.js inside it and open command prompt in it. Then run the following commands in this folder one-by-one:

> npm init
> npm install fast-gateway
> nodemon server.js

We will be using fast-gateway NPM module. Following will be the code of server.js file inside gateway folder.

const gateway = require("fast-gateway")

const server = gateway({
	routes: [
		{
			prefix: "/users",
			target: "http://localhost:3001"
		},
		{
			prefix: "/posts",
			target: "http://localhost:3002"
		}
	]
})

server.get("/serverApi", function (request, result) {
	result.send("Server API is called.")
})

server.start(3000).then(function () {
	console.log("Server is started running.")
})

The gateway server will be running on port 3000. Run the following URL in your web browser:

http://localhost:3000/users/getData

You will see the message “Get data from users.” So the gateway will automatically route all the requests prefixed with “/users” to the user microservice. Same goes for posts. Run the following URL in web browser:

http://localhost:3000/posts/getData

Now you will see the message “Get data from posts.” That’s how you can create microservices in NodeJS.

Custom middleware in Node JS

Middleware in Node JS is called before the request is handled by the router function. They stand between the router and the request. Middlewares are great when it comes to processing and validating request values before they reach the route.

Make sure you have downloaded and installed Node JS in your system first. You can download Node JS from here.

We are going to create a simple middleware that will add a new field “email” with the request.

# open terminal at desktop
# following command will create a folder
> mkdir nodejs-middleware

# open the folder in command prompt
> cd nodejs-middleware

# create a file named "server.js" in that folder
# following command will initialize the project
> npm init

# install express module
> npm install express

# start the server
> node server.js

Now in our server.js file, we will first start the server.

// server.js

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

// create instance of express module
const app = express()

// get the port number if already set, or 3000 otherwise
const port = process.env.PORT || 3000

// start the server at specified port
app.listen(port)

// test in browser
// http://localhost:3000

Make sure to restart the server using “node server.js” command. Now we will create our custom middleware.

// create a custom middleware
const myMiddleware = function (request, result, next) {
	// add "email" field with the request object
  	request.email = "support@adnan-tech.com"
  
  	// continue with the request
	next()
}

The following code will add the middleware to all the requests.

// attach middleware to all requests
app.use(myMiddleware)

The following code will add the middleware to specific requests only.

// attach middleware to specific request
app.get("/", myMiddleware, function (request, result) {
	// display "email" value on browser
  	result.send(request.email)
})

Restart the server and check in your browser: http://localhost:3000

External middleware in Node JS

You can create your middleware in a separate file as well in your Node JS server. Create a file named “external-middleware.js” at the root of your project and write the following code in it:

module.exports = function (request, result, next) {
	request.email = "adnan@gmail.com"
	next()
}

Then in your “server.js” file, use the middleware like the following:

const externalMiddleware = require("./external-middleware")
app.use(externalMiddleware)

To test this middleware, remove or comment out the previous middleware and refresh your browser again. Check out our android application’s tutorial we created on Node JS.

Practical difference between async await and callback

In one of our tutorial, we learned how to create a function in async await and how to create a function that uses callback.

Video tutorial:

Asynchronous

Let’s say you want to send an email to a user. Following is a Node JS code that sends an email using a callback:

const transport = nodemailer.createTransport({
	host: "",
	port: 465,
	secure: true,
	auth: {
		user: "",
		pass: ""
	}
})

transport.sendMail({
	from: "support@adnan-tech.com",
	to: "",
	subject: "Test",
	text: "Hello",
	html: "Hello"
}, function (error, info) {
	console.log("Mail sent: " + (new Date()))
})

console.log("I am called: " + (new Date()))

In your terminal, you will see that the “I am called:” message will be displayed instantly. But the “Mail sent:” message will be shown after a few seconds, once the mail is sent. It means that all lines of your code are executed at once, irrespective of the fact that the sendMail function will take some. The code is executed without waiting for any function to complete. This is asynchronous programming.

Synchronous

This can be converted into a synchronous function. Following is the synchronous version of the above code:

// the function must be async if await is used inside it

app.get("/sendMail", async function (request, result) {

	// send an email using a callback
	const transport = nodemailer.createTransport({
		host: "",
		port: 465,
		secure: true,
		auth: {
			user: "",
			pass: ""
		}
	})

	const info = await transport.sendMail({
		from: "support@adnan-tech.com",
		to: "",
		subject: "Test",
		text: "Hello",
		html: "Hello"
	})

	console.log("Mail sent: " + (new Date()))

	console.log("I am called: " + (new Date()))

	result.send("")
})

If you run the code now, you will have to wait for the sendMail function to finish before you see your log messages. This is synchronous programming.

Now you have understood the difference, let’s discuss where you should use async await and where you should use a callback.

When to use async await

You should use the async await pattern when the next line of your code is dependent on the previous lines. For example, “you have to insert the record in the database only if the mail is sent successfully”. In this case, you must use async await so that your code will wait for the mail to be sent successfully, then it will insert it in the database.

When to use callback

A callback will be helpful when your code sections are independent of each other. For example, “send an email to the user and insert the record in the database.” In this case, your insertion query should not have to wait for the mail to be sent. The mail should be handled in a callback.

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.

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.