5 JavaScript features to know

JavaScript brings tons of new features and it is hard to track of everything. I would like to share 5 features that incredibly useful.

1. Nullish Coalescing : Check undefined or null using ??

function calculatePrice(price, taxes, description) {
  taxes = taxes ?? 0.05
  description = description ?? "Default item"
  const total = price * (1 + taxes)
  console.log(`${description} with tax: CAD${total}`)

calculatePrice(100, 0.05, "First item") /* First item with tax: CAD105 */
calculatePrice(100, 0, "Second item") /* Second item with tax CAD100 */
calculatePrice(100, undefined, undefined)/* Default item with tax CAD105 */

2. Styling Console Log: add styles using %c

console.log(`%c${description} with tax: %cCAD${total}`, "font-weight: bold; color: green;", "color: red")

3. Optional Chaining: using ?

class Person {
  contructor(name, address, hobbies) {
    this.name = name
    this.address = address
    this.hobbies = hobbies


  print() {

function printPersonStreet(person) {
  /* check whether person is defined, then person.address is defined and person.address.street is defined */

const sugith = new Person("Sugith", {street: "1234 Edmonton St", city: "Edmonton"}, ["Cricket", "Badminton"])

sugith.print?.() /* print function will execute if it is available */

console.log(sugith.hobbies?.[0]) /*this will console log only if there is a hobbies array and the array has values*/



4. Object Shorthand: If you want to use the same key as the value variable.

const name = "Sugith"
const favouriteFood = "Rice"

const sugith = {
  name, favouriteFood


5. Defer/Async Loading: add defer to your script tag and you can place it in the head instead of the end of the body

<script src="script.js" defer></script>
This will run the script after the page renders

Laravel 8 with auth

laravel new laravel-vue

cd laravel-vue

--update the .env file with the database connection info 
-- update below settings to make sure the session is saving on a database.


php artisan migrate

composer require laravel/breeze --dev

php artisan breeze:install

npm install

npm run dev

php artisan session:table

php artisan cache:table 

php artisan migrate

php artisan serve

TURN ON debugger bar

composer require barryvdh/laravel-debugbar --dev

.env file


php artisan make:model Company -m

Nodejs deployment with PM2, NGINX and Let’s Encrypt

Install pm2

npm install -g pm2
#start the service with pm2
pm2 start [FILE_NAME]
#check the status 
pm2 status
#restart the service using pm2
pm2 restart [FILE_NAME]
#check your logs
pm2 logs
#flush/clear the logs
pm2 flush
#start the app when the server restarts
pm2 startup ubuntu

Before using NGINX

Turn on the firewall

:~# ufw status
Status inactive

#enable the firewall
ufw enable
ufw allow ssh

#Every port will be closed now. Because we activated the firewall
#We are going to turn on port 80 and 443

ufw allow http
ufw allow https

#Check the firewall status
ufw status

#Setup NGINX

#install NGINX
sudo apt install nginx

sudo nano /etc/nginx/sites-availble/default

Add the following to the location part of the server block

server_name yourdomain.com www.yourdomain.com;

location / {
  proxy_pass http://localhost:5000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade #http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;

#Check NGINX config
sudo nginx -t

# Restart NGINX
sudo service nginx restart

Add domain in Digital Ocean

In digital ocean, go to networking and add a domain

Add an A record for @ and for www your droplet

Register and/or setup domain from registrar

Go to your preferred domain registrar

Choose “custom namservers” and add these 3


Add SSL with LetsEncrypt

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

#Only valid for 90 days, test the renewal process with
certbot renew --dry-run

#Renew automatically- LetsEncrypt recommends that subscribers renew every sixty days.

#Add line into crontab -e
@monthly certbot renew

.Net Core MVC identity with PostgresSQL

Create the MVC project with authentication using visual studio.

Install these two libraries using NuGet

  • Npgsql.EntityFrameworkCore.PostgreSQL
  • Npgsql.EntityFrameworkCore.PostgreSQL.Design

Change the Providers in Startup.cs file


Change the connection string in appsettings.json


Run DB Migration


When trying to run the migration, cause you an error like

System.NullReferenceException: Object reference not set to an instance of an object. System.InvalidOperationException: No mapping to a relational type can be found for property ‘Microsoft.AspNetCore.Identity.IdentityUser.TwoFactorEnabled’ with the CLR type ‘bool’.

Delete the entire migration directory & regenerate a new initial migration

PM>Add-Migration Initial

This will create the migration table in your database. Then run


Learn Docker in 7 easy steps

Three things you need to know

  • Dockerfile – Blueprint for building a docker image
  • Image – Template for running docker container
  • Container – Running process
  • Kubernetes & Swarm can scale containers to the infinite workload.

If you are on Mac or Windows, download the Docker Desktop. Once you install, then you should get access to the docker from the command line.

First Command you should memorize : gives you all the containers running on your system.

  • docker ps

Install docker extension for VSCode.: gives you the language support.

The Dockerfile



FROM node:12


COPY package*.json

RUN npm install

COPY . . 



CMD ["npm", "start"]


The Image

How do we build the docker image

Go to your project and execute it. -t to tag the project with a useful name. Build a username on Docker Hub. Use that username followed by whatever you want to call this image. You can add a version number separated by a colon. Then add the path to your docker file

docker build -t sugith/demoapp:1.0 .

After it finishes, will give you a message “Successfully build [imageid]”

You can use this image as a base image to create other images or can use to run containers.

In real life to use this image, push the image to a container registry somewhere : dockerhub or your favourite cloud provider. The command you use to do that

docker push

Developer from somewhere can use docker pull to pull that image back down.

docker pull

The container

You can use the image id or the tag name

docker run b909406d737x

port forwarding to our local machine

docker run -p 5000:8080 b909406d737x

Go to localhost:5000 to see your app.

Docker container can still be running even after you close your terminal window. Go to the docker dashboard and stop the container.

When you stop the container any state or the data you created inside of it will be lost. But there can be a situation where you want to share data across between multiple containers. The preferred way to do this is with volumes.


Volume; is a dedicated folder on the host machine. A container can create files that can remounted in to future containers or multiple containers at the same time.

docker volume create shared-stuff
docker run \
> --mount source=shared-stuff,target=/stuff

The files will stick around even after all the containers are shut down.


Docker desktop can be handy in this situation

Click on the running containers, you can see all the logs and search through them. You can do this with the commandline with

docker exact

It will take you the root of the file system of the container. To keep your containers healthy, always write simple maintainable microservices


  • Keep 1 process per container

For multiple processes use multiple containers. Docker has a tool called docker-compose for that. It runs multiple Docker containers at the same time.



version: '3'
    build: .
      - "8080:8080"
    image: "mysql"
      MYSQL_ROOT_PASSWORD: password
      - db-data:/foo

docker-compose up

Find this file and run all the containers together. To shutdown the containers

docker-compose down

Go Lang

Install on Ubuntu

  • Download go .tar.gz file
tar -xvf go1.14.6.linux-amd64.tar.gz
sudo mv go /usr/local/
sudo vim ~/.profile and add this line
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
source ~/.profile
go version

Real-time data passing, Node.js, Express.js, postgres

Building a small app which gives the total count of people in a room and the flow of people enter and leave.

  1. Postgres DB and a table
  2. Postgres trigger on upsert or insert

CREATE TABLE realtime(
title character varying(128)

CREATE FUNCTION notify_realtime() RETURNS trigger
LANGUAGE plpgsql
AS $$
PERFORM pg_notify('addedrecord', NEW.title);

CREATE TRIGGER updated_realtime_trigger AFTER INSERT ON realtime

Setup a node project with express

mkdir testRealTime
cd testRealTime

npm init -y
npm install express pg socket.io --save

Node Server Code – index.js

const express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io").listen(server);
const pg = require ('pg');

let db = {
  user: 'postgres',
  host: 'localhost',
  database: 'realtime',
  password: 'postgres',
  port: 5432

let pg_client = new pg.Client(db);


let query = pg_client.query('LISTEN addedrecord');

io.sockets.on('connection', function (socket) {
  socket.emit('connected', { connected: true });

  socket.on('ready for data', function (data) {
    pg_client.on('notification', function(title) {
      socket.emit('update', { message: title });

app.get("/", function(req, res) {
  res.sendFile(__dirname + "/index.html");

server.listen(9001, function() {
  console.log("listening on *:9001");

Client side code – index.html

<html lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="http://localhost:9001/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

$(function () {
  let socket = io.connect('http://localhost:9001');
  socket.on('connected', function (data) {
    socket.emit('ready for data', {});

  socket.on('update', function (data) {

Vanilla JS Client-side Data Storage

Client-side storage

  • local storage
  • session storage
  • IndexedDB

local storage

Javascript APIs which allows to save data in the client side

*Don’t use cookies unless you have to. If you decide to use cookies please read the documentation on https://developer.mozilla.org/en-US/ before you proceed with the implementation.

Client-Side/Web Storage (Save Simple Data)

  • localStorage – persists data forever unless you clean it
  • sessionStorage – persists until you close the browser.


  • Both use key/value pair
  • no objects or arrays allowed
  • Domain Specific


localStorage.setItem('name', 'Sugith');

const name = localStorage.getItem('name');

const h1 = document.querySelector("#title");

name ? h1.textContent = `Welcome ${name}` : h1.textContent = `So sad nobody to welcome`;


Session Storage

Assume we are getting data from a form

const input = document.querySelector('#name');
const form = document.querySelector('form');
const submit = document.querySelector('#submitName');
const remove = document.querySelector('#removeName');

form.addEventListner('submit', (e) => {

submit.addEventListner('click', () => {
  sessionStorage.setItem('name', input.value);

remove.addEventListner('click', () => {

const name = sessionStorage.getItem('name');

const h1 = document.querySelector('#title');

name ? h1.textContent = `Welcome ${name}` : h1.textContent = `So sad nobody to welcome`;

When to use

  • localStorage
    • Shopping Cart(timers)
    • Non-privacy related info
  • sessionStorage
    • Privacy-related items
    • Shopping cart (temporary)
    • Temporary Info

IndexedDB – (Save Complex Data)

  • IndexedDB – Supports images, videos, allows you to do complex queries.


  • Full Database
  • Arrays, objects, images, videos
  • Structured data indexed with a key
  • Object oriented -> key/value pairs
  • Async to allow application operations
  • Supports all across browsers except Opera and anything below Explorer 11

Setup and connection

<form class="new-contact">
    <label for="name">Enter your First name:</label>
    <input type="text" id="firstName" required>
    <label for="name">Enter your Last name:</label>
    <input type="text" id="lastName" required>
  <button id="addName">New Contact</button>

<section class="contacts">

let db;
const firstNameInput = document.querySelector("#firstName");
const lastNameInput = document.querySelector("#lastName");
const form = document.querySelector("form");

window.onload = () => {
  let request = window.indexedDB.open('contacts', 1);

  request.onerror = function() {
    console.log('Database failed to open');

  request.onsuccess = function() {
    console.log("Database opened successfully");

    db = request.result;

Defining Index and Schema

request.onupgradeneeded = function(e) {
  let db = e.target.result;

  let objectStore = db.createObjectStore('contacts', {
    keyPath: 'id',
    autoIncrement: true

  //create Schema
  objectStore.createIndex('firstName', 'firstName', { unique: false});
  objectStore.createIndex('lastName', 'lastName', { unique: false});

  console.log('Database setup complete');


Add Items to IndexedDB

function addData(e) {

  let newItem = { 
    firstName: firstNameInput.value, 

  let transaction = db.transaction(['contacts'], 'readwrite');

  let objectStore = transaction.objectStore('contacts');

  let request = objectStore.add(newItem);

  request.onsuccess = () => {
    firstNameInput.value = '';
    lastNameInput.value = '';

  transaction.oncomplete = () => {
    console.log('Transaction completed on the database');

  transaction.onerror = () => {
    console.log('Transaction not completed, ERROR');

Retrieve Items from IndexedDB

const list = document.querySelector("ul");
function displayData() {
  while (list.firstChild) {

  //create a cursor
  let objectStore = db.transaction('contacts').objectStore('contacts');

  objectStore.openCursor().onsuccess = function (e) {
    let cursor = e.target.result;

      if(cursor) {
        let listItem = document.createElement('li');
        let first = document.createElement('span');
        let last = document.createElement('span');



        first.textContent = cursor.value.firstName;
        last.textContent = cursor.value.lastName;

        listItem.setAttribute('data-contact-id', cursor.value.id);

        let deleteButton = document.createElement('button');
        deleteButton.textContent = 'Delete';

      } else {
        if(!list.firstChild) {
          let listItem = document.createElement('li');
          listItem.textContent = 'No contacts store.';
    console.log('contacts displayed');

Delete Items from IndexdDB

deleteButton.onclick = deleteItem;

function deleteItem(e) {

  let contactId = Number(e.target.parentNode.getAttribute('data-contact-id'));
  let transaction = db.transaction(['contacts'], 'readwrite');
  let objectStore = transaction.objectStore('contacts');
  let request = objectStore.delete(contactId);

  transaction.oncomplete = () => {
    console.log(`Contact ${contactId} is deleted`);

    console.log('Transaction completed on the database');

    if(!list.firstChild) {
      let listItem = document.createElement('li');
      listItem.textContent = 'No contacts store.';


  • Cannot sync with service back-end data
  • At the mercy of browser data wipe out
  • No use if browser in private mode
  • At the mercy of quota limit or browser rules


  • Service Workers (offline)
  • Cache API

Alternatives for local storage

  • LocalForge
  • Dexie.js
    • dexie.org
  • ZangoDB
  • JsStore

Dexie.js (Dexie.org)

const db  = new Dexie('MyDatabase');

//create a database
  contacts: '++id, firstName, lastName' 

//Insert data
db.contacts.put({firstName: "Sugith", lastname: "Karunaratne"});