In this post i will show you how to build a simple url shortener service like bit.ly below is the final product.
Getting Started
This is the list of npm packages we need to use to make this url shortener in this tutorial:
Express
A web application framework for the Nodejs applications.mongoose
Used to model the objects for Nodejs for the Mongodatabase.shortid
Unique short id generator package for nodejs.cors
CORS is a node.js package for providing a Connect/Express
middleware that can be used to enable CORS with various
options.valid-url
valid-url is used to check the is the entered url is an valid url or not.
Tutorial
Before starting the tutorial you must have on mongodb cloud account.because i'm not showing here how to create a mongodb cloud account You just visitMongodb.
Let's Beginπ
Make a project directory to anywhere where you easily excess it anytime.
$mkdir url_shortner
$cd url_shortner
Intialize a node project by npm
$npm init -y
Install required packages
$npm i express mongoose cors shortid valid-url
Create a index.js file
const express = require('express');
const mongoose = require('mongoose');
const shortid = require('shortid');
const cors = require('cors');
const URL = require('./models/URL');
const bp = require('body-parser');
var validUrl = require('valid-url');
const app = express();
/*SET APP VIEW ENGINE TO EJS*/
app.set('view engine','ejs')
app.use(cors());
//set app static file directory to public
app.use(express.static('public'))
app.use(bp.json())
app.use(express.urlencoded({extended: true}));
mongoose.connect('<Mongodb cloud url>/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('/:uid',async (req,res) => {
const url = await URL.findOne({shorten_id:req.params.uid});
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://easyurly.herokuapp.com/${result.shorten_id}`});
}
})
const port = process.env.PORT || 3000;
app.listen(port,()=>{
console.log(`Server listen at ${port} & DB is also connected!`);
})
Make a static public & views directory in root directory of the project
$mkdir public views
create a css file inside public folder for our view file.
also create a views directory and create a index.ejs file inside it which renders an webpage on index route of our node application
/views/index.ejs
<!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 href="https://dev.to/ammyy9908" 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('http://localhost:5000/',{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>
/public/style.css
: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;
}
Create a Mongoose Model
Using Schema class of mongoose package .to do this create a file called URL.js inside a model directory and create a schema using Schema class and pass to model function by giving collection name & schema
/models/URL.js
const {model,Schema} = require('mongoose');
const URLSchema = new Schema({
url:{
type: 'string',
required: true,
},
shorten_id:{
type: 'string',
required: true,
}
});
module.exports = model('url',URLSchema);
π Thanks for Reading....
Clone Github Repo
Top comments (0)