Create a URL Shortener/Mapper in NodeJs.
Sumit Kumar

Create a URL Shortener/Mapper in NodeJs.

Hello ?, In this Article i will show you how to make a url shortener/mapper using javascript(nodeJs)

Prerequisites

1.NodeJs must be installed.

2.A IDE or Code Editor(VS code).

3.A basic knowledge of Nodejs, ExpressJS, Mongodb and basic of conditionals.

Here is the final Product Overview


Let's begin by creating and initialising a Nodejs project wherever you want

e.g


 $mkdir url-shortener && cd url-shortener && npm init -y && code .        

The above commands can be execute one at time but to reduce some time i use && operator to run multiple commands at a time.

First command create a directory called url-shortener for you and second command move your current working directory position to new directory , third command is initialise the directory as a Nodejs Project and the last command opening the VS Code in the current directory by specifying .(current directory) you can omit last command if you use different code editor.

After this install some required Js libraries we going to use through this project. and that are:

  1. express:-> A web application framework for the Nodejs applications.

2. mongoose:-> Used to model the objects for Nodejs for the Mongodb.

3. shortid:-> Unique short id generator package for nodejs application.

4. cors:->use to handle CORS Policy error comes when client request resource from other origin than server origin.

5. valid-url:-> valid-url is used to check the is the entered url is an valid url or not.


Note ?? :In this tutorial i use mongodb cloud Database you can either work by installing mongo in local machine so to use mongo as a cloud db make sure you have one mongodb cloud database account.

So now just install all above libraries execute npm install command like we use pip in python.

$npm i express mongoose valid-url shortid cors        

After installing all above packages just open package.json file in code editor and find a scripts field and make a start command by typing.


"scripts": {
    
    "start": "node index.js"
   
  }        

"start":"node index.js" in scripts object in this file.


Ok now create our starting file(index.js) which we declared in start script which is the entry point of our project just like main function in C,Java.

now type the following code in this (index.js) file, i explain the code below:


const express = require('express');
const mongoose = require('mongoose');
const shortid = require('shortid');
const cors = require('cors');
const bp = require('body-parser');
var validUrl = require('valid-url');
const URL = require('./models/URL');





const app = express();



app.set('view engine','ejs')
app.use(cors());
app.use(express.static('public'))
app.use(bp.json())
app.use(express.urlencoded({extended: true}));





    mongoose.connect('mongodb+srv://Sumit:[email protected]/URLSHORTNER',{useNewUrlParser:true,useUnifiedTopology:true},()=>{
        console.log("Database connected!");
    });


// Database connection




// Routes


app.get('/',async (req,res) => {
    res.render('index',{title:'URLY-url shortner'});
})


app.get('/:lid',async (req,res) => {
    const url = await URL.findOne({shorten_id:req.params.lid});
    if(!url){
        return res.status(500).json({error:"Invalid Shorten URL!"});
    }
    res.redirect(url.url);
})
app.post('/',async (req,res) => {
    const {url} = req.body;
/// first validate the url
const isValid = validUrl.isUri(url)
console.log(isValid);
if(!isValid) {
    return res.status(400).json({error: 'Invalid URL'});
}
//check is there is same url present in database if yes give the shorten id of the url
   const isExists = await URL.findOne({url:url});
   if(!isExists) {
       const newURL = new URL({
           url,
           shorten_id:shortid.generate()
       })
       const result = await newURL.save();
       return res.send({shorten_url:`https://localhost:3000/${result.shorten_id}`});
   }
   
})



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


app.listen(port,()=>{
    
   
        console.log(`Server listen at ${port} & DB is also connected!`);
    
});
        


So let start from line 1 to 6, where we just importing(requiring) our installed packages.


const express = require('express')
const mongoose = require('mongoose');
const shortid = require('shortid');
const cors = require('cors');
const bp = require('body-parser');
var validUrl = require('valid-url');;        


and line 7 is the Model we creating for Storing URL Meta data in database

const app = express(), by declaring this we tells that app is now become the instance of express app means all methods & properties of express module an now accessible from app variable only.


app.set('view engine','ejs'

app.use(cors());

app.use(express.static('public'))

app.use(bp.json())

app.use(express.urlencoded({extended: true}));)        


by using app.set() & app.use() we declaring some middleware to the app(express app). like in first middleware we telling that our view engine is EJS and same app.use(cors()) is telling that we accept cors through the app. app.use(express.static('public')) tells that our static file like css,images are placed in public directory.

app.use(bp.json()):->use to tell our server accepting data as json in client body from the request.



   mongoose.connect('mongodb+srv://Sumit:[email protected]/URLSHORTNER',{useNewUrlParser:true,useUnifiedTopology:true},()=>
        console.log("Database connected!");
    });        


this line just connecting our database with our application by specifying DB URL and table name or collection name.


Now make three request Routes ,2 GET & another one is POST.

GET:/:->used to just displaying our frontend index.ejs file stores in views directory.


app.get('/',async (req,res) => 
    res.render('index',{title:'URLY-url shortner'});
})        


GET:/:lid


app.get('/:lid',async (req,res) => 
    const url = await URL.findOne({shorten_id:req.params.lid});
    if(!url){
        return res.status(500).json({error:"Invalid Shorten URL!"});
    }
    res.redirect(url.url);
})        

This get route accepting link short id as a param, here is the important part of accepting a short url and redirecting the user to that original url.

Here we extracting link-id from url and finding a url from database where shorten_id = lid

if we getting a record then just redirecting the user to that original url using res.redirect(url.url)

else just send an error in response with status 500


POST:/ here we just accepting full lengthy original url a url from the user in request body


app.post('/',async (req,res) => 
    const {url} = req.body;
/// first validate the url
const isValid = validUrl.isUri(url)
console.log(isValid);
if(!isValid) {
    return res.status(400).json({error: 'Invalid URL'});
}
//check is there is same url present in database if yes give the shorten id of the url
   const isExists = await URL.findOne({url:url});
   if(!isExists) {
       const newURL = new URL({
           url,
           shorten_id:shortid.generate()
       })
       const result = await newURL.save();
       return res.send({shorten_url:`https://localhost:3000/${result.shorten_id}`});
   }
   
})        

Here first getting url from req.body and validate this url using valid-url middleware already imported which returns a boolean by accepting url in paramater.

if found a valid url then we need to add an extra checkpoint if this url already present with some shorten id then just find that shorten_id and concatenate with our base_url+shorten_id which converts a short url like:https://localhost:3000/shorten_id


else send the error in response by saying Invalid URL

if there is no record of this url present in database means we need to create one in db by generating a unique id and store i to db and concatenate that shorten id into our base_url like if the shorten id is ax1234 the concatenating with hostname gives :https://localhost:3000/ax1234

And at the last of this file we just obtaining a port for our application and run the app on this port by using app.listen() method.


const port = process.env.PORT || 3000


app.listen(port,()=>{
    
   
        console.log(`Server listen at ${port} & DB is also connected!`);
    
});        

So, This is end of the backend for the project now moving to the frontend by creating a file called index.ejs file which is same like html by having capability to manipulate server data using some code syntax.


No alt text provided for this image

As you see there is a file in views directory called index.ejs file where we just sending the original url to the server by using a form. and the code snippet is :



<!DOCTYPE html
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/style.css">
    <title><%= title %> </title>
</head>
<body>
    <nav>
        <div class="contaier__nav">
            <div class="brand__name">
                <strong>URLY</strong>
            </div>
            <div class="links">
                <a  class="btn-about" title="Feature not supported">About Devloper</a>
            </div>
        </div>
    </nav>
    <div class="container">
        <div class="container__left">
            <h1>Map Your Long Urls with URLY</h1>
            <p>A URL shortener/mapper built with NodeJS</p>
            <div class="input__div">
                <form><input type="text" name="url" placeholder="Shorten Your URL" autocomplete="off"><input type="submit" value="Shorten URL"></form>
                
            </div>
            <!-- <a href="#/" class="btn btn-primary">Explore</a> -->
        </div>
    </div>
    <div class="output">


        <a href="" class="shorten__url">Demo</a>
    </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js"></script>
    <script>
        const form = document.querySelector('form');
        form.addEventListener('submit', async (e)=>{
            e.preventDefault();
            const url =form.url.value;
            if(url){


               try {
                const r = await axios.post('https://localhost:3000/',{url:url});
            if (r.status === 200) {
                document.querySelector('.output').style.display = 'flex';
                
                   document.querySelector('.output').innerHTML = `<p href="#" class="shorten__url">${r.data.shorten_url}</p>`;
                   
                   form.reset();
            }
        } catch (e) {
            if (e.response && e.response.data) {
                alert(e.response.data.error);
                const error = e.response.data.error;
                showError(error);
                
            }
        }
            }
        })


        // add event on output to copy the url
        document.querySelector('.output').addEventListener('click', function (e){
            navigator.clipboard.writeText(e.target.textContent).then(()=>{
                e.target.innerHTML = '<p>URI COPIED!</p>';
                setTimeout(function () {
                    document.querySelector('.output').style.display = 'none';
                },3000);
            })
        })


        const showError = (err)=>{
            document.querySelector('.output').innerHTML = `<p>${err}</p>`;
        }



    </script>
</body>
</html>        

Ok i forget one thing create a static css file in public directory so we can styles our ejs file.



:root
    --primary-color:linear-gradient(to right, #92fe9d 0%, #00c9ff 100%);
}
body{
    margin: 0;
    padding: 0;
    background-color: #fafafa;
}


nav{
width: 100%;
height: 65px;
}
.contaier__nav{
    max-width: 1277px;
    display: flex;
    justify-content: space-between;
    margin: 0 auto;
    align-items: center;
    height: 100%;
}
.brand__name > strong{
    background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
    font-family: "ProximaNova ExtraBold","Helvetica Neue",Helvetica,Arial,sans-serif;
    font-size: 22px;
    margin-left: 10px;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    transition:all 3s ease-in-out;
    background-size: 300%;
}
.links{
    display: flex;
    justify-content:space-around;
    flex: .3;
}
.links > a{
    text-decoration: none;
    font-family: "ProximaNova ExtraBold","Helvetica Neue",Helvetica,Arial,sans-serif;
    padding: 10px 32px;
    display: inline-flex;
    justify-content:flex-start;
    align-items:center;
    margin-right: 10px;
}
.btn-about{
    background: var(--primary-color);
    color: #fff;
    border-radius: 5px;
}
.disabled{
    cursor: not-allowed;
    background-color: #ccc;
}
.container{
    width: 100%;
    display: flex;
    align-items: center;
    margin-top: 35px;
    justify-content:center;
    margin-bottom: 35px;
    height: 75vh;


}
.container > div{
    max-width: 95%;
    padding-bottom: 35px;
}
.container__left{
    flex: 1;
    display:flex;
    flex-direction: column;
    justify-content:space-evenly;
    align-items: center;
}


.container__left > h1{
    font-family: "ProximaNova ExtraBold","Helvetica Neue",Helvetica,Arial,sans-serif;
    font-size: 55px;
    color: rgb(245, 14, 64);
    background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
    -webkit-background-clip: text;
    font-weight: 900;
    -webkit-text-fill-color: transparent;
    animation: gradients 10s infinite linear;
    background-size: 300%;
}
@keyframes gradients {
    0%{
       background-position: 0 50%;


    }
    50%{
        background-position: 100% 50%;
    }
    100%{
        background-position: 0 50%;


    }
}
.container__left > p{
    color: #56575b;
    font-weight: 400;
    font-size: 24px;
    font-family: "ProximaNova Regular","Helvetica Neue",Helvetica,Arial,sans-serif;
    margin-top: 15px;
}
.btn{
    font-size: 20px;
    padding: 18px 30px;
    background: var(--primary-color);
    border-radius: 6px;
    font-size: inherit;
    color: #fff;
    text-decoration: none;
    outline: none !important;
    font-family: "ProximaNova Regular","Helvetica Neue",Helvetica,Arial,sans-serif;
}
.input__div{
    width: 65%;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content:center;
    margin-top: 10px;
}
.input__div > form{
    flex: 1;
    display: flex;
    justify-content:space-evenly;
    align-items: center;
    width: 100%;
}
.input__div > form > input[type=text]{
flex: .5;
height: 45px;
padding-left: 10px;
border: 2px solid #84fab0;
outline: none;
border-radius: 4px;
}
.input__div > form > input[type=submit]{
flex:.4;
height: 50px;
padding-left: 10px;
background:linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
color: #fff;
border: 0;
outline: none;
border-radius: 5px;
text-transform: uppercase;
font-weight: 700;
transition:all 3s ease-in-out;
}
.input__div > form > input[type=submit]:hover{
  
    background:linear-gradient(60deg, #8fd3f4 0%, #84fab0 100%);
    
    
    }


.shorten__url{
    font-size: 12px;
    font-family: "ProximaNova Regular","Helvetica Neue",Helvetica,Arial,sans-serif;
    color: #fff;
    text-decoration: none;
}
.output{
    width: 100%;
    display: none;
    background-color: #262626;
    margin: 0;
    height: 45px;
    justify-content:center;
    align-items: center;
    transition: all .5s ease-in-out;
    position: fixed;
    bottom:0;
    cursor: pointer;
    
}
.output > p{
    font-size: 12px;
    font-family: "ProximaNova Regular","Helvetica Neue",Helvetica,Arial,sans-serif;
    color: #fff;
    text-decoration: none;
}


@media (max-width:768px){
    .links{
        display: none;
    }
    /* .container__left > h1{
        
        font-size: 31px;
    }
    .container__left > p{
        
        font-size: 14px; */
    /* } */
}        


If you found this helpful then connect me on:


要查看或添加评论,请登录

社区洞察

其他会员也浏览了