DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Developing a simple URL shortener with node, express, ejs and mysql
Mohammad Abdul Alim
Mohammad Abdul Alim

Posted on

Developing a simple URL shortener with node, express, ejs and mysql

A URL shortener is a pretty simple system that shortens longer URLs. On hitting the short URL, a user is automatically redirected to the actual URL. The main advantage of this is a user can share a short form of a very long URL. Today I would like to develop a simple URL shortener with node, express, ejs and mysql.

Features

Our web app will have the following features:

  • Shorten lonnger URLs
  • Redirect to main URL upon clicking the shorter one
  • Copy the shorter URL to use anywhere
  • Show the number of time a particular URL has been shorten

Project Setup

We will need the followings for this project:

  • Node runtime environment
  • MySQL as a database which also be obtained by using XAMPP or similar packages
  • express application framework
  • ejs to generate HTML template views
  • shortid to generate unique and short ids for URLs
  • nodemon as a watcher to obtain auto reloading the project on each save

Project Description

At first lets create a folder named url-shortener in our local machine and go to that folder. Now its time to create the package.json file and install necessary packages. Following commands will do so:

npm init -y
npm i express ejs mysql shortid
npm i --save-dev nodemon
Enter fullscreen mode Exit fullscreen mode

We also need to update the script property with "dev": "nodemon index.js" which means on running npm run dev nodemon will run our entry file. So our package.json file will look like:

{
  "name": "url-shortener",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ejs": "^3.1.6",
    "express": "^4.17.1",
    "mysql": "^2.18.1",
    "shortid": "^2.2.16"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now lets create the index.js file in our root directory along with two directories named public and views to store assets and ejs files respectively.

Lets describe the index.js file gradually. At first we import all the packages and start the express server.

const express = require("express");
const shortid = require("shortid");
const mysql = require("mysql");
const app = express();

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Now if we run the npm run dev command then in http://localhost:3000/ of our browser express will run but we need to specify routes. Before that we specify the view engine and static file path.

app.set("view engine", "ejs");
app.use(express.static(__dirname + "/public"));
app.use(express.urlencoded({ extended: false }));
Enter fullscreen mode Exit fullscreen mode

Now we define our home route like this:

app.get("/", (req, res) => {
    res.render("home.ejs");
});
Enter fullscreen mode Exit fullscreen mode

Here it says whenever a request is created to the root path, it will show the home template file as the response. So inside the views directory we create the home.ejs file and write the following:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>URL Shortener</title>
    <link rel="stylesheet" type="text/css" href="/styles/home.css" />
</head>
<body>
    <div class="container">
        <h2>URL Shortener</h2>
        <p>Convert long URL to shorter one with a single click. Its easy, simple and absolutely free!</p>
        <form action="/shortUrl" method="post">
            <input type="url" placeholder="Enter the URL" name="fullUrl" required />
            <input type="submit" value="Convert" />
        </form>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Here we have added a css file named home.css which should remain in a folder named styles of the public directory. This means we have to create the styles directory inside public directory and create home.css inside it. Then we write the follwing css code:

.container {
    width: 50%;
    margin: auto;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 10px;
}

h2 {
    margin: 0;
}

p {
    max-width: 350px;
}

input[type="url"] {
    height: 28px;
    width: 250px;
    padding-left: 8px;
    border-radius: 4px;
    border: 1px solid #000;
}

input[type="submit"] {
    padding: 10px 20px;
    color: #fff;
    background-color: #349ded;
    border: none;
    border-radius: 4px;
    margin-left: 5px;
}

input[type="submit"]:hover {
    cursor: pointer;
    opacity: 0.85;
}

.span-link {
    padding: 10px 20px;
    border-radius: 4px;
    background-color: #349ded;
    color: #fff;
}

.result-container {
    background-color: #dddcdc;
    padding: 10px;
    min-width: 200px;
    display: flex;
    justify-content: space-around;
}

a {
    text-decoration: none;
}

.copy-span:hover {
    cursor: pointer;
    opacity: 0.75;
}
Enter fullscreen mode Exit fullscreen mode

Now upon saving our code our browser should look like this:
Image description
Now if we add a URL in the input section and click Convert, it will not work because we have not defined our route /shortUrl for <form action="/shortUrl" method="post">. In order to create this route we need to create our database and table at first. I have used XAMPP to do so. After running Apache and MySQL processes of XAMPP control panel we go to http://localhost/phpmyadmin/ and create a database named url_shortener. Then we create a table named url which has the following structure:
Image description
We can see that the table has four properties namely an auto increment id, fullUrl, shortUrl and counts which stores the number of times a particular URL gets shortened. Now its time to connect our database. We add the followings in our index file:

const db = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "",
    database: "url_shortener"
});

db.connect(err => {
    if(err) {
        console.log("Error connecting to DB");
        return;
    }
    console.log("Connceted to DB");
});
Enter fullscreen mode Exit fullscreen mode

After this its time to create our /shorturl post route. Here our logic is pretty much simple. Our request body contains a parameter named fullUrl that is given as an input by the user. At first we query to the db with that parameter whether an entry exists. If not then we create a new entry with that fullUrl, its generated shortid and counts 1. Then we pass shortUrl and counts as object to a new view named result.ejs. If the entry exists then we simply increase its counts by 1 and pass shortUrl and counts as object to the view. Lets see our route now:

app.post("/shorturl", (req, res) => {
    const fullUrl = req.body.fullUrl;
    if (!fullUrl) {
        return res.sendStatus(404);
    }
    db.query('SELECT * FROM `url` WHERE `fullUrl` = ?', [fullUrl], (error, results) => {
        if (error) {
            console.log("we got error");
            return;
        }

        if (results.length === 0) {
            const short = shortid.generate();
            const url = { fullUrl: req.body.fullUrl, shortUrl: short, counts: 1 };
            db.query('INSERT INTO `url` SET ?', url, (err, res) => {
                if (err) {
                    console.log("Error creating table");
                    return;
                }
            });
            res.render("result.ejs", { shortUrl: short, times: 1 });
        } else {
            const _short = results[0].shortUrl;
            const _counts = results[0].counts;
            db.query('UPDATE `url` SET `counts` = ? WHERE `shortUrl` = ?', [_counts + 1, _short], (err, res) => {
                if (err) {
                    console.log("Error updating table");
                    return;
                }
            });
            res.render("result.ejs", { shortUrl: _short, times: _counts + 1 });
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

In the same time we create result.ejs file inside views directory and add following:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>URL Shortener</title>
    <link rel="stylesheet" type="text/css" href="/styles/home.css" />
</head>
<body>
    <div class="container">
        <h2>URL Shortener</h2>
        <p>Your shortened URL is</p>
        <div class="result-container">
            <span><a id="short-url" href="<%= `/${shortUrl}` %>" target="_blank"><%= shortUrl %></a></span>
            <span onclick="copyUrl()" class="copy-span" id="copy-action">Copy</span>
        </div>
        <p>It has been converted <%= times %> times</p>
        <br />
        <a href="/"><span class="span-link">Try Another</span></a>
    </div>
    <script>
        const copyUrl = () => {
            const copyTextarea = document.getElementById("short-url").href;
            navigator.clipboard.writeText(copyTextarea);
            document.getElementById("copy-action").innerHTML = "Copied";
        };
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now upon saving our files lets copy https://www.youtube.com/watch?v=dwKSRsmpYjc&ab_channel=INSIDE, paste it to our input field and click Convert. We see something like this:
Image description
Here by clicking the Copy field we can copy our short URL and clicking the short URL we can go to a new tab but unfortunately it will not redirect to the actual URL because we have not defined our corresponding route yet. So lets define it:

app.get("/:shortUrl", (req, res) => {
    db.query('SELECT * FROM `url` WHERE `shortUrl` = ?', [req.params.shortUrl], (error, results) => {
        if (error) {
            return res.sendStatus(404);
        }

        if (results.length === 0) {
            res.render("error.ejs");
        } else {
            res.redirect(results[0].fullUrl);
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

Here we are sending a dynamic parameter with our route path and looking for an entry with that short URL in our database. If an entry exists then we simply redirect to the fullUrl of it. Otherwise we render an error.ejs page that shows an error page and asks to visit the home page. Its code is:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Error Page</title>
    <link rel="stylesheet" type="text/css" href="/styles/home.css" />
</head>
<body>
    <div class="container">
        <h2>URL Shortener</h2>
        <p>The URL you entered does not exist!</p>
        <br />
        <a href="/"><span class="span-link">Visit Home Page</span></a>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Thus we have developed a simple URL Shortener website very easily. The full code can be found here. Please feel free to share your thoughts rerarding it.

Happy coding πŸ˜€πŸ˜€πŸ˜€

Top comments (5)

Collapse
 
saravanasai profile image
Saravana Sai

Really a great work

Collapse
 
alim1496 profile image
Mohammad Abdul Alim

Thanks a lot

Collapse
 
emrancub profile image
Md. Emran Hasan • Edited on

Good Article. Thank you, sir.

Collapse
 
alim1496 profile image
Mohammad Abdul Alim

You are welcome. If possible, share with your network.

Collapse
 
suhakim profile image
sadiul hakim

πŸ‘

Stop sifting through your feed.

Find the content you want to see.

Change your feed algorithm by adjusting your experience level and give weights to the tags you follow.