Create URL Shortener in NodeJS

nodejs-url-shortener

A URL shortener is a great way to share any links you want with your friends, families of followers. Nowadays you can find many URL shorteners in the market, some of them are paid some of them are free.

You can track the clicks and view of your shared links through these URL shorteners. If you are a developer and want to add a custom URL shortener in your application or website or you are just student who wants to learn how to create one then you are in the right place.

In this tutorial, we are going to create to our own custom URL shortener using NodeJS & MongoDB.

Prerequisite.

To follow along with this tutorial you need to have at least basic knowledge of NodeJS & Express.js. To store the data for our application I will use MongoDB as a database.

If you want to use a different database then you are free to do that. For this tutorial, I am going to use //mlab.com/ which provides you MongoDB database as service so that you don’t have to install the MongoDB locally.

Go to the link and create your account. it’s free of cost. It provides you 500MB of the database to test application.

Creating a Project Directory.

Create a new directory for our project. Open that directory in the terminal and fire this command to initialize a NodeJS project.

> npm init -y

This will initialize the directory with a file called package.json

Installing Dependencies.

We need to install the following dependencies to make our app work,

1). Express.js – expressjs is a framework for Nodejs which will ease our development effort.

2). mongoose – It is an ORM which will help us to query our MongoDB database.

3). dotenv – It is a package which let us use a .env file where we can store our important piece of information such as MongoDB connection string, database username & password, etc.

So in your terminal enter the following command and hit enter,

> npm install express dotenv mongoose

This command will install all the dependencies based on your Internet speed.

MongoDB Database Setup.

As you know I will online MongoDB service provide by the mlab.com. If you have already installed the MongoDB database then you can skip this step.

Step 1: Go to the mlab.com & create your account. After successful login to your account, you will see an interface like this,

Step 1- Mlab Dashboard

Step 1- Mlab Dashboard

Now click on the create a new button to create a new database.

Step 2: Now select the plan type and click on the continue button,

Step 2 - Select plan type

Step 2 – Select plan type

Step 3: Select a region & click on the continue button

Step 3 - selecting a region

Step 3 – selecting a region

Step 4: Enter any suitable name for your database & click on the continue button.

Step 4 - database name

Step 4 – database name

Step 5: After step 4 you will all the details related to your plan. Now click on the submit button to create the database. After the successful creation of your database, you will redirect to your dashboard.

Step 6: Now click on the database instance you have created. After that, you will some connection string like this,

mongodb://<dbuser>:<dbpassword>@ds151416.mlab.com:51416/nodejs-url-shortener

Now you need to create a user which will have access to your database instance. For that click on the users tab & at the right side you will find the create user button. After creating the user, you need to replace the dbuser & dbpassword in the connection string with your actual database user’s details.

For example, If the username is test & password is also test then the connection string would look like this,

mongodb://test:test@ds151416.mlab.com:51416/nodejs-url-shortener

Creating Env File.

I have already discussed that I am going to use a .env file to store some important data so that the user cannot have direct access to it.

Create a .env file at the root of directory & add these lines in it,

MONGODB_URL='mongodb://test:test123@ds119996.mlab.com:19996/shortlink'
BASE_URL='//localhost:5000/'

The MONGO_DB & BASE_URL  are the keys which will be used to access their data.

Creating & Running a Server For Our Application.

We require to create a server which will handle all the incoming request for our application.

const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const apiController = require('./controllers/apiController')

// To access our .env file values
require("dotenv").config();

// Set up port
const port = process.env.PORT || 5000;

const app = express();


// Set BodyParser middleware
app.use(bodyParser.json());

// Get the DB conection string
const dbURL = process.env.MONGODB_URL;

// Connect to the database
mongoose
  .connect(dbURL, { useNewUrlParser: true })
  .then(() => console.log("MongoDB connected..."))
  .catch(err => console.error(err));


// For testing purpose only
app.get('/hello', function (req, res, next) {
    return res.json({msg: 'Hello world'});
})

// Redirct any route which includes /api to apiRoute controller
app.use("/", apiController);


// Start listening
app.listen(port, () => console.log("Server started at port " + port));

You can see in the above code that we have set up the two routes, one is for the base URL  which is only for the testing purpose & another is for the handling all the URL shorting methods

We will not bloat this server.js file with all the business logic code instead I will create a different controller & model for that.

Creating The Model For Storing Shortened URLs.

We need to create a model for our URLs which we are going to store in our MongoDB. I will store all the moodels in the models directory, So we need to create a directory named models. Inside the models folder create a new file called ShortUrls.js.

const mongoose = require('mongoose')
const Schema = mongoose.Schema;

//Create Schema
const ShortUrlsSchema = new Schema({
    url: {
        type: String,
        required: true
    },
    urlCode: {
        type: String
    },
    
    hits: {
        type: Number,
        default: 0
    },
    createdDate: {
        type: Date,
        default: Date.now
    }
})

module.exports = ShortUrls = mongoose.model('ShortUrls', ShortUrlsSchema)

Controller For Creating Shortened URLs.

Let’s create the controller which will short our URLs. I will store my controller in the constrollers directory. So create a new directory named controllers in the project root. Inside the controllers directory create a new file named apiController.js

const express = require("express");
const router = express.Router();

require("dotenv").config();

const ShortUrls = require("../models/ShortUrls");

// get base URL
const baseUrl = process.env.BASE_URL;

/**
 * @route POST api/short
 * @desc Short a given URL
 * @access public
 */
router.post("/short", (req, res, next) => {
 
  let shortUrl;

  if (isEmpty(req.body)) {
    res
      .status(400)
      .json({ success: false, msg: "Data missing!", data: req.body });
  }

  // Create a shorturl object
  shortUrl = new ShortUrls({
    url: req.body.url,
  });

  createAndSaveShortUrl(shortUrl, res);

});

/**
 * @route GET api/urlID
 * @desc Redirect to actual url based on URL ID
 * @access public
 */
router.get("/:id", (req, res, next) => {
  const urlCode = req.params.id;

  console.log('url id', urlCode);
  

  ShortUrls.findOne({ urlCode: urlCode }, (err, urlObj) => {
    if (err) {
      console.log(err);

      res.status(500).json({ success: false, msg: "Internal Server Error!" });
      return;
    }

    if (isEmpty(urlObj)) {
      res.status(404).json({ success: false, msg: "URL does not exist!" });
      return;
    }

    const redirectTo = urlObj.url;

    // Update the hits counter of url
    ShortUrls.updateOne(
      { urlCode: urlCode },
      { $inc: { hits: 1 } },
      (err, model) => {
        if (err) {
          console.log(err);
          return;
        }
        console.log(urlObj);

        // Redirect to actual URL
        res.redirect(redirectTo);
      }
    );
  });
});



function createAndSaveShortUrl(shortUlrObj, res) {
  // Generate a random string to replace the url
  let randomStr = generateRandomString();
  // Check if the random string already exist in DB
  ShortUrls.findOne({ urlCode: randomStr }, (err, url) => {
    if (err) {
      console.log(err);
    } else if (url == null || isEmpty(url)) {
      console.log("url obj", url, randomStr);

      shortUlrObj.urlCode = randomStr;

      // Not a duplicate
      shortUlrObj.save(err => {
        if (err) {
          res.status(400).json({ success: true, msg: err });
        }
        res.status(200).json({ success: true, url: baseUrl + randomStr });
      });
    } else {
      // Generated random string already exist in the DB
      // Try once again
      saveShortUrl(shortUlrObj);
    }
  });
}

function generateRandomString() {
  var length = 6,
    charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
    retVal = "";
  for (var i = 0, n = charset.length; i < length; ++i) {
    retVal += charset.charAt(Math.floor(Math.random() * n));
  }
  return retVal;
}

function isEmpty(obj) {
  if (obj == null) return true;
  return Object.entries(obj).length === 0 && obj.constructor === Object;
}

module.exports = router;

Here you can see we have two routes, One route is for creating the URL which will assign a random ID to any URL second one for actually redirecting the shortened URL to actual URL, we also maintain a counter for URL which indicates how many times people have actually gone through that URL.

Testing The Application.

To test our application to see if our code actually works I am going to use the Postman to test the API.

Postman API test result

Postman API test result

Let’s check if the database entry is created in the MongoDB.

MongoDB entry or shortened URL

MongoDB entry or shortened URL

Now if you go to the shortened URL you get in the Postman then it will redirect you to the actual URL.

In the next tutorial, will try to create a frontend in ReactJS where we can consume this API. So subscribe to our newsletter to get the updates.

If you have any suggestions or if you face any problem please write it down in the comment box…

Happy Coding…:)

 

Click Here To Download The Complete Project Files

ES10-ES2019 New Features For JavaScript Developers

Ropali Munshi
Ropali Munshi
Ropali Munshi is fullstack PHP Developer. He is passionate developer who loves to learn and expirement with new programming languages , libraries and frameworks. Nowdays he is more into the JavaScript realm.

1 Comment

  1. Great article, just what I wanted to find.

Leave a Reply

Your email address will not be published. Required fields are marked *

You Don't Want To Miss It!

Please subscribe to our newsletter. Every week I share tips, tricks, tutorials, free books & video course directly in your inbox.