Creating a URL Shortener using Node.Js

In this blog we will build a URL shortener using Node.Js and MongoDb.

If you are not familiar with a URL Shortener, it is basically a tool that can make a long URL like this one https://alexcodetuts.com/2020/02/05/asp-net-core-3-1-clean-architecture-invoice-management-app-part-1/ into something like this https://tinyurl.com/y6zsg2ms

Let’s start building our project. Create a folder somewhere to store our files. In my case I created a url-shortener folder in documents.

Open a terminal or command prompt and navigate to your foler. Then Execute this command to create our package.json file and just press enter with other questions.

npm init

Then we need to install some node packages for our project

npm install body-parser cors dotenv express mongoose nodemon valid-url

Create a folder called database create index.js file inside the folder. We will put our database connection on this file.

const mongoose = require('mongoose');
require('dotenv').config();

const connect = async () => {
    return mongoose.connect(process.env.DB_HOST, {
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useCreateIndex: true
    });
}

module.exports = { connect }

Now we need to create a file for our schema of url data. In the root folder, create a folder called models then create a file url.js

const mongoose = require('mongoose');

const schema = mongoose.model('Url', {
    url: {
        type: String,
        required: true
    },
    alias: {
        type: String,
        unique: true,
        required: true
    },
    clickCount: Number
});

module.exports = schema

We will need two routes for our project. One is for saving the url and another one is for redirecting.

Create a new folder called routes. Then add two files shorUrl.js and urlRedirect.js

Here is the code for shortUrl.js.

const express = require("express");
const validUrl = require("valid-url");
const { nanoid } = require("nanoid");

require('dotenv').config();

const baseUrl = process.env.BASE_URL;

const Url = require('../models/url.js');

const route = express.Router();

route.post("/", async(req, res) => {
    const { url } = req.body;

    if(!validUrl.isUri(baseUrl)) {
        return res.status(500).json("Internal Server error. Try again later.");
    }

    if(!validUrl.isUri(url)) {
        return res.status(400).json("Invalid Url. Please make sure to give a valid url.");
    }

    try {
        const existingUrl = await Url.findOne({url});
        if(existingUrl) {
            return res.status(200).json({
                url: existingUrl.url,                
                shortUrl: `${baseUrl}/${existingUrl.alias}`
            })
        }
        const newUrl = new Url({
            url,
            alias: nanoid(),
            clickCount: 0
        });

        await newUrl.save();
        return res.status(200).json({
            url: newUrl.url,
            shortUrl: `${baseUrl}/${newUrl.alias}`
        });

    }
    catch (err) {
        console.error(err.message);
        return res.status(500).json("Internal Server error " + err.message);
    }
})

module.exports = route;

We use valid-url library to check if the user inputted a valid url and we use the library called nanoid to generate an id which will use as a code for our short url.

Here is the code for urlRedirect.js

const express = require("express");

const Url = require('../models/url.js');

const route = express.Router();

route.get("/:alias", async(req, res) => {
    const { alias } = req.params;

    try {
        const url = await Url.findOne({alias: alias});
        if(url) {
            const { clickCount } = url;
            url.update({clickCount: clickCount + 1});
            return res.redirect(url.url);
        } else {
            return res.status(400).json("The url doesn't exists in our system.");
        }
    }
    catch (err) {
        console.error(err.message);
        return res.status(500).json("Internal Server error " + err.message);
    }
})

module.exports = route;

The code is very straight forward. Check if the url is in the database and then if it is redirect to the save url otherwise return an error.

Now to put all together, we need our index.js in the root folder. Create the index.js and copy this code.

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');

require('dotenv').config();

const shortUrlRoute = require("./routes/shorturl")
const urlRedirectRoute = require("./routes/urlRedirect")

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extend: true}));
app.use(cors());

require('./database').connect();

app.use("/", urlRedirectRoute)
app.use("/api/", shortUrlRoute);

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
    console.log(`Listening at ${PORT}`);
});

If you notice, we use dotenv in our code to store our configs. So we need to create an .env file in the root folder.

DB_HOST=CHANGE_THIS_WITH_YOUR_DB_URL
BASE_URL=http://localhost:3000
PORT=3000

Now to run our application we need to update the scripts in package.json. Here is the copy of my package.json

{
  "name": "url-shortener",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "mongoose": "^5.10.15",
    "nanoid": "^3.1.18",
    "nodemon": "^2.0.6",
    "valid-url": "^1.0.9"
  }
}

Now we can test our application by running this command

npm start

Then use a postman to test our api

Copy the value of the shortUrl and paste it to your browser. Then if everything works well you should be redirected to the url.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s