Monthly Archives: April 2018

Building a Mini Invoicing Application with Vue and NodeJS - JWT Authentication and Sending Invoices

By Chris Nwamba

Retrieving Token after Login

In the previous parts of the series, we looked at how to create the User Interface of our Invoicing Application that allowed users to create and view existing invoices. In this final part of the series, we will look at the last important parts that haven’t been visited.

  • Persisting User Session on Client
  • Single View for Invoices
  • Sending Invoices using Node Mailer

Prerequisites

To follow this article adequately, you need the following:

  • Node installed on your machine.
  • NPM installed on your machine.
  • To have read the first and second parts of this series.

To confirm your installation, run the following command:

node --version
npm --version

If you get their version numbers as results then you’re good to go.

Persisting User Sessions on Client Using JWTokens

To verify that our application is secure and only authorized users can make requests, we are going to make use of JWTokens. JWT – JSON Web Tokens consist of a 3 part string containing the header, payload and signature of the request. In the core idea of it is to create a token for each authenticated user to use when performing requests to the backend server.

Creating JWTokens
To get started, change directory into the existing invoicing``-``app – if you don’t already have it, feel free to go to the previous article chapters or grab the code from the Github repository. After doing that, install the jsonwebtoken node module that will be used to create and verify our JSON Web Tokens:

cd invoicing-app 
npm install jsonwebtoken nodemon --save

nodemon is a node module that restarts your server once file changes occur.

Now, update the server.js file by adding the following:

    // server.js

    // import node modules
    [...]
    const jwt = require("jsonwebtoken");

    // create express app
    [...]
    app.set('appSecret', 'secretforinvoicingapp'); // this will be used later

Next thing to do is to tweak the /register and /login routes to create tokens and pass them back once a user has successfully registered or logged in. To do this, add the following to your server.js:

    // server.js

    // edit the /register route
    app.post("/register", multipartMiddleware, function(req, res) {
      // check to make sure none of the fields are empty
      [...]
      bcrypt.hash(req.body.password, saltRounds, function(err, hash) {
        // create sql query 
        [...]
        db.run(sql, function(err) {
          if (err) {
            throw err;
          } else {
            let user_id = this.lastID;
            let query = `SELECT * FROM users WHERE id='${user_id}'`;
            db.all(query, [], (err, rows) => {
              if (err) {
                throw err;
              }
              let user = rows[0];
              delete user.password;
              //  create payload for JWT
              const payload = {
                user: user 
              }
              // create token
              let token = jwt.sign(payload, app.get("appSecret"), {
                expiresInMinutes: "24h" // expires in 24 hours
              });
              // send response back to client
              return res.json({
                status: true,
                token : token
              });
            });
          }
        });
        db.close();
      });
    });

    [...]

We do the same in the /login route:

    // server.js
    app.post("/login", multipartMiddleware, function(req, res) {
      //  connect to db 
      [...]
      db.all(sql, [], (err, rows) => {
        // attempt to authenticate the user
        [...]
        if (authenticated) {
          //  create payload for JWT
          const payload = { user: user };
          // create token
          let token = jwt.sign( payload, app.get("appSecret"),{
            expiresIn: "24h" // expires in 24 hours
          });
          return res.json({
            status: true,
            token: token
          });
        }

        return res.json({
          status: false,
          message: "Wrong Password, please retry"
        });
      });
    });

Now that this is done, the next thing to do is to test it. Run your server using the command:

nodemon server.js

Head over to Postman to test the endpoint and this what you get:

Now that we’ve seen how to create tokens on successful login and registration. The next part is to very token for incoming requests. To do this, add the following middleware above the routes you want to protect:

    // server.js

    [...]
    // unprotected routes

    [...]
    // Create middleware for protecting routes
    app.use(function(req, res, next) {
      // check header or url parameters or post parameters for token
      let token =
        req.body.token || req.query.token || req.headers["x-access-token"];
      // decode token
      if (token) {
        // verifies secret and checks exp
        jwt.verify(token, app.get("appSecret"), function(err, decoded) {
          if (err) {
            return res.json({
              success: false,
              message: "Failed to authenticate token."
            });
          } else {
            // if everything is good, save to request for use in other routes
            req.decoded = decoded;
            next();
          }
        });
      } else {
        // if there is no token
        // return an error
        return res.status(403).send({
          success: false,
          message: "No token provided."
        });
      }
    });

    // protected routes 
    [...]

Middleware Rejecting Requests without Token

Using JWTokens with Frontend
In the SignUp.vue where authentication was set up, we need to store the token obtained from the server and the user data in the localStorage so that the this can persist across different pages when the user is using our application. To do this, update the login and register methods of your frontend/src/components/SignUp.vue to look like this:

    // frontend/src/components/SignUp.vue
    [...]
    export default {
      name: "SignUp",
      [...]
      methods:{
        register(){
          const formData = new FormData();
          let valid = this.validate();
          if(valid){
            // prepare formData
            [...]
            // Post to server
            axios.post("http://localhost:3128/register", formData)
            .then(res => {
              // Post a status message
              this.loading = "";
              if (res.data.status == true) {
                // store the user token and user data in localStorage
                localStorage.setItem('token', res.data.token);
                localStorage.setItem('user', JSON.stringify(res.data.user));
                // now send the user to the next route
                this.$router.push({
                  name: "Dashboard",
                });
              } else {
                this.status = res.data.message;
              }
            });
          }
          else{
            alert("Passwords do not match");
          }
        }
        [...]

Let’s also update the login method:

    // frontend/src/components/SignUp.vue
        login() {
          const formData = new FormData();
          formData.append("email", this.model.email);
          formData.append("password", this.model.password);
          this.loading = "Signing in";
          // Post to server
          axios.post("http://localhost:3128/login", formData).then(res => {
            // Post a status message
            console.log(res);
            this.loading = "";
            if (res.data.status == true) {
              // store the data in localStorage
              localStorage.setItem("token", res.data.token);
              localStorage.setItem("user", JSON.stringify(res.data.user));
              // now send the user to the next route
              this.$router.push({
                name: "Dashboard"
              });
            } else {
              this.status = res.data.message;
            }
          });

The localStorage only accepts Strings so to get around that, JSON.stringify to convert to a JSON String

So the user data and token are stored in the Local storage and then the user is routed to the next component. Previously, user data was passed using route parameters, but now, we get the data from the local Storage. Let’s see how this changes our components.

The Dashboard component earlier looked like this:

    // frontend/src/components/Dashboard.vue

    <script>
    import Header from "./Header";
    import CreateInvoice from "./CreateInvoice";
    import ViewInvoices from "./ViewInvoices";
    export default {
      name: "Dashboard",
      components: {
        Header,
        CreateInvoice,
        ViewInvoices,
      },
      data() {
        return {
          isactive: 'create',
          title: "Invoicing App",
          user : (this.$route.params.user) ? this.$route.params.user : null
        };
      }
    };
    </script>

What this meant was that, when a user signs in/up, they go redirected to the Dashboard page and then the user property of the Dashboard component is updated accordingly. The main issue with this though is that if the user then decides to refresh the page, there’s no way to tell who the user is because now, the this.$route.params.user that was used to identify the user earlier on no longer exists.

Edit your dashboard component to now use the browsers localStorage like this:

    // frontend/src/components/Dashboard.vue

    import Header from "./Header";
    import CreateInvoice from "./CreateInvoice";
    import ViewInvoices from "./ViewInvoices";
    export default {
      name: "Dashboard",
      components: {
        Header,
        CreateInvoice,
        ViewInvoices,
      },
      data() {
        return {
          isactive: 'create',
          title: "Invoicing App",
          user : null,
        };
      },
      mounted(){
        this.user = JSON.parse(localStorage.getItem("user"));
      }
    };

Now, when we sign in and refresh the page, we still have the user data. This is not all though, when requests are being made, we have to add the token to our requests.

Let’s take a look at the ViewInvoices component. Here’s what the JavaScript for the component looks like:

    // frontend/src/components/ViewInvoices.vue
    <script>
    import axios from "axios";
    export default {
      name: "ViewInvoices",
      components: {},
      data() {
        return {
          invoices: [],
          user: '',
        };
      },
      mounted() {
        this.user = JSON.parse(localStorage.getItem('user'));
        axios
          .get(`http://localhost:3128/invoice/user/${this.user.id}`)
          .then(res => {
            if (res.data.status == true) {
              console.log(res.data.invoices);
              this.invoices = res.data.invoices;
            }
          });
      }
    };
    </script>

Right now, when you attempt to view invoices for a logged in user, you get this error:

Error Retrieving Invoices Due To Absence of Tokens

This is because the route invoice/user/:user_id route of the application is now protected with the token middleware as was set up earlier. To fix the error, let’s add it to our request as follows:

    // frontend/src/components/ViewInvoices.vue
    <script>
    import axios from "axios";
    export default {
      name: "ViewInvoices",
      components: {},
      data() {
        return {
          invoices: [],
          user: '',
        };
      },
      mounted() {
        this.user = JSON.parse(localStorage.getItem('user'));
        axios
          .get(`http://localhost:3128/invoice/user/${this.user.id}`,
            {
              headers: {"x-access-token": localStorage.getItem("token")}
            }
          )
          .then(res => {
            if (res.data.status == true) {
              console.log(res.data.invoices);
              this.invoices = res.data.invoices;
            }
          });
      }
    };
    </script>

When you save this and go back to your browser, you can now fetch the invoices successfully:

Token Added to Request to Retrieve invoices

Single View for Invoices

At the moment, when the TO INVOICE button is clicked, nothing happens. To fix this, create a SingleInvoice.vue and edit it as follows:

    <template>
      <div class="single-page">
        <Header v-bind:user="user"/>
        <!--  display invoice data -->
        <div class="invoice">
          <!-- display invoice name here -->
          <div class="container">
            <div class="row">
                <div class="col-md-12">
                  <h3>Invoice #{{ invoice.id }} by {{ user.company_name }}</h3>
                  <table class="table">
                    <thead>
                      <tr>
                        <th scope="col">#</th>
                        <th scope="col">Transaction Name</th>
                        <th scope="col">Price ($)</th>
                      </tr>
                    </thead>
                    <tbody>
                      <template v-for="txn in transactions">
                        <tr :key="txn.id">
                          <th>{{ txn.id }}</th>
                          <td>{{ txn.name }}</td>
                          <td>{{ txn.price }} </td>
                        </tr>
                      </template>
                    </tbody>
                    <tfoot>
                      <td></td>
                      <td style="text-align: right">Total :</td>
                      <td><strong>$ {{ total_price }}</strong></td>
                    </tfoot>
                  </table>
                </div>
              </div>
            </div>
          </div>
      </div>
    </template>

The SingleInvoice component has the following template. The v-for directive is used to allow us loop through all the fetched transactions for the particular invoice. Let’s see how to obtain the invoice and transaction data from the backend server.

The component structure looks like below. We first import the necessary modules and components. When the component is mounted , a POST request using axios is made to the backend server to fetch the data. When the response is obtained, we assign them to the respective component properties.

    <script>
    import Header from "./Header";
    import axios from "axios";
    export default {
      name: "SingleInvoice",
      components: {
        Header
      },
      data() {
        return {
          invoice: {},
          transactions: [],
          user: "",
          total_price: 0
        };
      },
      methods: {
        send() {}
      },
      mounted() {
        // make request to fetch invoice data
        this.user = JSON.parse(localStorage.getItem("user"));
        let token = localStorage.getItem("token");
        let invoice_id = this.$route.params.invoice_id;
        axios
          .get(`http://localhost:3128/invoice/user/${this.user.id}/${invoice_id}`, {
            headers: {
              "x-access-token": token
            }
          })
          .then(res => {
            if (res.data.status == true) {
              this.transactions = res.data.transactions;
              this.invoice = res.data.invoice;
              let total = 0;
              this.transactions.forEach(element => {
                total += parseInt(element.price);
              });
              this.total_price = total;
            }
          });
      }
    };
    </script>

Notice: There’s a send() method in the method that is currently empty. As you move on through the article, you would get a better understanding as to why and how to add the necessary functionality

The component has the following scoped styles:

    <!-- frontend/src/components/SingleInvoice.vue -->
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1,
    h2 {
      font-weight: normal;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #426cb9;
    }
    .single-page {
      background-color: #ffffffe5;
    }
    .invoice{
      margin-top: 20px;
    }
    </style>

Now, if you back to the application and click the TO INVOICE button in the View Invoices tab, you get view below:

Single Invoice View

Sending Invoice via Email

This is the final step of our mini-invoicing application is to allow our users send invoices to whoever they like. To keep this part as simple as possible, we are going to use the nodemailer module. This module allows you send emails to specified recipients using an email on the backend server. To get started, first install the module:

npm install nodemailer

Now that the module is installed, we update the server.js file as follows:

    // server.js
    // import node modules
    [...]
    let nodemailer = require('nodemailer')

    // create mail transporter
    let transporter = nodemailer.createTransport({
      service: 'gmail',
      auth: {
        user: 'COMPANYEMAIL@gmail.com',
        pass: 'userpass'
      }
    });

    // create express app
    [...]

Note : This email will be set on the backend server and will be the account sending emails on behalf of the user. Also, you will need to temporarily allow non-secure sign-in for your Gmail account for testing purposes here

Temporarily Allowing Less Secure Apps

    // server.js

    // configure app routes
    [...]
    app.post("/sendmail", multipartMiddleware, function(req, res) {
      // get name  and email of sender
      let sender = JSON.parse(req.body.user);
      let recipient = JSON.parse(req.body.recipient);
      let mailOptions = {
        from: "COMPANYEMAIL@gmail.com",
        to: recipient.email,
        subject: `Hi, ${recipient.name}. Here's an Invoice from ${
          sender.company_name
        }`,
        text: `You owe ${sender.company_name}`
      };
      transporter.sendMail(mailOptions, function(error, info) {
        if (error) {
          return res.json({
            status: 200,
            message: `Error sending main to ${recipient.name}`
          });
        } else {
          return res.json({
            status: 200,
            message: `Email sent to ${recipient.name}`
          });
        }
      });
    });

At this point, we have configured the emails to work when a POST request is made to the /sendmail route. The next thing we want to do is to allow the user perform this action on the frontend. To do this, let’s update the SingleInvoice component by doing the following:

Add Form to Collect Recipient Information
A from will be added below the display of the invoices. To allow the user send a copy of the invoice to interested parties.

    <!-- frontend/src/components/SingleInvoice.vue -->

    <template>
     <Header v-bind:user="user"/>
        <!--  display invoice data -->
        <div class="invoice">
          <!-- display invoice name here -->
          <div class="container">
            <div class="row">
              <div class="col-md-12">
                // display invoice
              </div>
            </div>
            <div class="row">
              <form @submit.prevent="send" class="col-md-12">
                <h3>Enter Recipient's Name and Email to Send Invoice</h3>
                <div class="form-group">
                  <label for="">Recipient Name</label>
                  <input type="text" required class="form-control" placeholder="eg Chris" v-model="recipient.name">
                </div>
                <div class="form-group">
                  <label for="">Recipient Email</label>
                  <input type="email" required placeholder="eg chris@invoiceapp.com" class="form-control" v-model="recipient.email">
                </div>
                <div class="form-group">
                    <button class="btn btn-primary" >Send Invoice</button>
                    {{ loading }}
                    {{ status }}
                </div>
              </form>
            </div>
          </div>
        </div> 
    </template>

Also, the component properties are updated as follows:

    // frontend/src/components/SingleInvoice.vue

    <script>
    import Header from "./Header";
    import axios from "axios";
    export default {
      name: "SingleInvoice",
      components: {
        Header
      },
      data() {
        return {
          invoice: {},
          transactions: [],
          user: '',
          total_price: 0,
          recipient : {
            name: '',
            email: ''
          },
          loading : '',
          status: '',
        };
      },
      methods: {
        send() {
          this.status = "";
          this.loading = "Sending Invoice, please wait....";
          const formData = new FormData();
          formData.append("user", JSON.stringify(this.user));
          formData.append("recipient", JSON.stringify(this.recipient));
          axios.post("http://localhost:3128/sendmail", formData, {
            headers: {"x-access-token": localStorage.getItem("token")}
          }).then(res => {
            this.loading = '';
            this.status = res.data.message
          }); 
        }
      },
      mounted() {
        // make request to fetch invoice data
      }
    };
    </script>

Now, when you go back to view a single invoice in the browser and add the recipients details, you get the following result:

Sent Invoice

The recipient gets an email that looks like this:

Email Received by Recipient

Note: You can further edit the email sent to the recipient to be as expressive as you may like using this guide

Conclusion

In this part of the series, we looked at how to use JWTokens and the Browser’s Local Storage to keep users signed in. We also created the view for a single invoice and showed how to use nodemailer to send emails to recipients about invoices. Feel free to leave a comment below if you have any questions. Here’s a link to the full Gitbhub repository if interested.

Source:: scotch.io

Going Offline: Designing An Ideal Offline Experience With Service Workers By Jeremy Keith

Ben Nadel reviews Going Offline: Designing an Ideal Offline Experience with Service Workers by Jeremy Keith. Like the other A Book Apart installments, Going Offline is pragmatic and appropriately in-depth without ever being overwhelming. It is an enjoyable and accessible primer on how Service Workers can help you create a more positive user experience (UX)….

Source:: bennadel.com

Weekly Node.js Update - #15 - 04.27, 2018

By Tamas Kadlecsik

Weekly Node.js Update - #15 - 04.27, 2018

Below you can find RisingStack‘s collection of the most important Node.js news, updates, projects & tutorials from this week:

Node v10.0.0 (Current) is released

Node.js 10.0.0 is the seventh major Node.js release since the launch of the Node.js Foundation. In October of 2018, it will become the next Active Long Term Support branch.

Partially in celebration of the N-API native addon API graduating from experimental status, this Node.js release also comes with a coordinated experimental release of Node-ChakraCore that has full support for N-API and advances the Time-Travel innovation with easier getting started using a VSCode extension.

Node v10 is Here – Feature Breakdown!

Node v10 was cut from the master this week, and became the latest version. Check out our latest post where our CEO, Tamas Kadlecsik discusses its most important features and the way it will affect developing Node.js apps.

Announcing npm@6

npm@6 was announced in coordination with the Node.js v10. This major update to npm includes powerful new security features for every developer who works with open source code. Read on to understand why this matters.

Sharing Code Between Projects: Lessons Learned In The Trenches

As a team grows and code lines multiply, sharing code is increasingly difficult due to continuous updates. What is the solution for sharing and syncing common components of code between your projects?

These 6 essential tools will release, version, and maintain your NPM modules for you

A suite of automation tools can be employed to free you from the shackles of keeping dependencies up-to-date, ensuring code quality, and releasing new versions of your software.

Weekly Node.js Update - #15 - 04.27, 2018

Build a “Serverless” SMS FAQ Bot with Node.js, MessageBird and StdLib in 6 Minutes

In just 6 minutes you will be shown how to build a bot that will automatically answer questions from an FAQ via SMS using StdLib, MessageBird, and QnAMaker.ai!

JavaScript Prototype Poisoning Vulnerabilities in the Wild

One vulnerability that has come up a lot is prototype poisoning by copying properties. Let’s dive deep into this type of vulnerability, and see how it can be prevented in one’s own projects, starting with a little background.

Weekly Node.js Update - #15 - 04.27, 2018

Create A YouTube-style Recommendation Engine With Node & Vue

In this article, we’ll walk you through the steps of building a simple application that suggests personalized videos to a user based on videos the user has recently uploaded: the user uploads videos and in return gets a feed of related videos.

Dynaflow: Our Open-source Node Driver for DynamoDB

Read the story of how (and why) developers at Vice Tech built Dynaflow, their high-level DynamoDB driver.

Previously Node.js Updates:

In the previous Weekly Node.js Update, we collected great articles, like

  • Transactions and promises in Node.js;
  • Consumer Driven Contract Testing with Node.js & Pact;
  • Yarn vs npm – which Node package manager to use in 2018?;
  • Debugging JavaScript/TypeScript Node apps with Chrome DevTools, VS Code and WebStorm 🐞🔫;

& more…

We help you to stay up-to-date with Node.js on a daily basis too. Check out our Node.js news page and its Twitter feed!

Source:: risingstack.com

What's New in Node 10 "Dubnium"

By Samuel Oloruntoba

Node.js 10.0.0 is the seventh major Node.js release since the launch of the Node.js Foundation. In October of 2018, it will become the next Active Long Term Support branch.

Node.js version 10 is out. Yay!! This is the next LTS (Long term support) version of Node codename “Dubnium” (it’s a rare radioactive element and the most stable isotope in nature, probably a nod to the stability of Node.js v10).

You can also keep up with the release of Node.js.

There are quite a few cool features with this release so let’s just jump into it.

Stable HTTP/2

HTTP/2 support was first released in version 8 of Node. The initial release was a bit buggy, but with this version comes stability of HTTP/2 in Node.

Currently, Hapi and Koa support HTTP/2 in Node v10, but you can still use express-http2-workaround for express.

Let’s see a quick example of how to HTTP/2 in Node using Hapi.js:

const fs = require('fs')
const Hapi = require('hapi')
const http2 = require('http2')

const server = new Hapi.Server()
server.connection({
  listener: http2.createServer(),
  host: 'localhost',
  port: 3000
})

server.route({
  method: 'GET',
  path:'/hello',
  handler: function (request, reply) {
    return reply('Hello, World!')
  }
})

server.start((err) => {
  if (err) console.error(err)
  console.log(`Started ${server.connections.length} connections`)
})

Better support for ESM modules

Node came with it’s own module system and it’s what we’ve used for years. Node’s module system is called CommonJS (you’ve probably heard of it).

However, CommonJS is not quite common as it’s only suited to the Node environment. If you try to run a CommonJS file in the browser, you’ll get a barrage of red flags in devtools.

Along comes EcmaScript 6 which brought a module system to JavaScript. With this release Node.js supports this module system.

What this means is that we can now do stuff like

import { resolve } from 'path'

and

export { default } from 'os'

For this to work, you need to adhere to the new .mjs file naming convention. You can read all about it on GitLayer

We also get a standardized global object (window alternative)

Standardized error codes

Before v10, once a new or strange error occurred, we literally had to copy-paste the message in Google and rely on their impeccable ability to find your answer in StackOverflow’s archive.

With this release, all errors in node have a code attached to them. This gives us more error handling power and flexibility and removes a lot of guessing from your StackOverflow game. Node Foundation did a better job explaining it, so go check it out.

N-API is no longer experimental

If you don’t know what N-API is let me explain.

N-API is an API for building native Addons

You don’t have to know any C or C++ to do this as it is a part of Node.js. It is intended to insulate Addons from changes in the underlying JavaScript engine and allow modules compiled for one version to run on later versions of Node.js without recompilation.

It was first released in Node v8, but it’s now stable to use.

V8 and OpenSSL updates

Node.js runs on the V8 JavaScript engine, the same engine that runs Google’s Chrome. With Node v10, V8 got an upgrade bump to v6.6. Doing this means that we now have support for async generators and iterators in Node. No transpilation or experimental flag necessary.

OpenSSL also got a bump to v1.1.0. This means that we have access to TLS 1.3 which allows faster SSL handshakes and more security. It also means we get the ChaCha20 cipher and Poly1305 authenticator.

npm v6

It’s not a Node update without npm. With Node v10 comes npm v6 and npm stepped up their game. This new release comes with

  • More focus on security, and npm has it baked in.
  • You can use the npm audit command to find security vulnerabilities.
  • I haven’t tested this out, but it claims to be 17x faster.
  • There’s also npm optimizations for continuous integration (CI) environments so builds can run faster.
  • More visible integrity metadata for package tampering.
    etc.

New methods

New methods added to Node v10 include:

  • String#trimStart/trimEnd to remove unwanted characters either at the beginning or end of strings.
  • RegExp: s (dotAll) & u (unicode) flags, named captures, lookbehinds
  • Optional (e) in try {} catch(e) {}
  • Finally, console.table.
  • EventEmitter.prototype.off() will serve as an alias for EventEmitter.prototype.removeListener()
  • The WHATWG URL API is now a global. Meaning we have access to the same URL API as the browser.

Deprecations

  • Using require() to access several of Node.js’ own internal dependencies will emit a runtime deprecation.
  • The crypto.createCipher() and crypto.createDecipher() methods have been deprecated.
  • Passing more than one argument to assert.fail() will emit a runtime deprecation warning
  • Use of non-string values for process.env has been deprecated in documentation.

Conclusion

That was a quick wrap of what’s new in Node.js v10. You can read the official changelog for more information on this release.

Below are also a few resources you could check for more information.

Have a great day. Oh! let us know your thoughts on this release in the comments. What’s the most exciting feature?

Source:: scotch.io