URL Shortener Project Documentation
URL Shortener Project Documentation
Table of Contents
Introduction
Project Structure
Installation Guide
Usage Instructions
API Documentation
Code Snippets
Contributing
License
1. Introduction
The URL Shortener project is a web application that allows users to shorten long URLs into more manageable and shareable links. The application also provides analytics for the shortened URLs, such as the number of clicks and visit history.
Technologies Used
Node.js: JavaScript runtime environment.
Express.js: Web framework for Node.js.
MongoDB: NoSQL database for storing URL data.
EJS: Templating engine for rendering HTML.
Nanoid: Library for generating unique short IDs.
2. Project Structure
The project is organized as follows:
controllers/: Contains the logic for handling requests.
url.js
: Handles URL shortening and analytics.
models/: Contains the data models.
url.js
: Defines the schema for URL data.
public/: Static files like CSS, JavaScript, and images.
routes/: Defines the routes for the application.
staticRouter.js
: Handles the home page route.url.js
: Handles URL-related routes.
views/: Contains the EJS templates for rendering HTML.
home.ejs
: The main view for the home page.
.gitignore: Specifies files and directories to be ignored by Git.
connect.js: Handles the connection to the MongoDB database.
index.js: The main entry point of the application.
package.json: Lists project dependencies and scripts.
package-lock.json: Automatically generated file for dependencies.
3. Installation Guide
Prerequisites
Node.js and npm installed on your machine.
MongoDB installed and running locally.
Steps to Set Up the Project
Clone the Repository
git clone <repository-url> cd urlsortner
Install Dependencies
npm install
Set Up MongoDB
Ensure MongoDB is running on your local machine.
The application connects to MongoDB at
mongodb://
localhost:27017/urlshortener
.
Run the Application
node index.js
Access the Application
- Open your browser and navigate to
http://localhost:3000
.
- Open your browser and navigate to
4. Usage Instructions
Shortening a URL
Visit the home page at
http://localhost:3000
.Enter the long URL in the input field and submit.
The application will generate a shortened URL and display it on the page.
Accessing Analytics
Use the shortened URL to access the analytics.
Visit
http://localhost:3000/url/analytics/<shortid>
to view the number of clicks and visit history for the shortened URL.
5. API Documentation
Generate Short URL
Endpoint:
POST /url
Request Body:
{ "url": "https://example.com" }
Response:
{ "shortID": "abc123" }
Get Analytics
Endpoint:
GET /url/analytics/:shortid
Response:
{ "totalclicks": 10, "visithistory": [ { "timestamp": "2023-10-01T12:00:00.000Z" } ] }
6. Code Snippets
index.js
const express = require('express');
const path = require('path');
const app = express();
const urlrouter = require("./routes/url");
const { connectDB } = require('./connect');
const URL = require('./models/url');
const staticRoute = require('./routes/staticRouter');
const port = 3000;
app.set('view engine', 'ejs');
app.set('views', path.resolve("./views"));
connectDB('mongodb://localhost:27017/urlshortener').then(() => {
console.log('Connected to DB');
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use('/url', urlrouter);
app.use('/', staticRoute);
app.get('/url/:shortid', async (req, res) => {
const shortid = req.params.shortid;
const entry = await URL.findOneAndUpdate({
shortID: shortid
},
{
$inc: { totalclicks: 1 },
$push: { visithistory: { timestamp: new Date().toISOString() } }
});
res.redirect(entry.redirectURL);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
connect.js
const mongoose = require('mongoose');
async function connectDB(url) {
return mongoose.connect(url);
}
module.exports = {
connectDB,
};
models/url.js
const mongoose = require('mongoose');
const urlschema = new mongoose.Schema({
shortID: {
type: String,
required: true,
unique: true
},
redirectURL: {
type: String,
required: true
},
totalclicks: {
type: Number,
required: true
},
visithistory: [{
timestamp: {
type: Date,
required: true
}
}]
}, { timestamps: true });
const URL = mongoose.model('url', urlschema);
module.exports = URL;
controllers/url.js
const { nanoid } = require('nanoid');
const URL = require('../models/url');
async function handleGenerateShorturl(req, res) {
const body = req.body;
if (!body.url) return res.status(400).json({ message: 'URL is required' });
const shortID = nanoid(4);
await URL.create({
shortID: shortID,
redirectURL: body.url,
totalclicks: 0,
visithistory: []
});
return res.render('home', { id: shortID });
}
async function handleGetAnalytics(req, res) {
const shortID = req.params.shortid;
const result = await URL.findOne({ shortID });
return res.json({
totalclicks: result.totalclicks,
visithistory: result.visithistory
});
}
module.exports = {
handleGenerateShorturl,
handleGetAnalytics
};
routes/url.js
const express = require('express');
const { handleGenerateShorturl, handleGetAnalytics } = require('../controllers/url');
const router = express.Router();
router.post('/', handleGenerateShorturl);
router.get('/analytics/:shortid', handleGetAnalytics);
module.exports = router;
routes/staticRouter.js
const express = require('express');
const URL = require('../models/url');
const router = express.Router();
router.get("/", async (req, res) => {
const allurls = await URL.find({});
return res.render('home', { urls: allurls });
});
module.exports = router;
views/home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
color: #333;
}
h1 {
color: #444;
text-align: center;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 500px;
margin: 20px auto;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
input[type="text"] {
width: calc(100% - 22px);
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
button {
background-color: #28a745;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #218838;
}
/* Table Styles */
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f8f9fa;
font-weight: bold;
}
tr:hover {
background-color: #f1f1f1;
}
/* URL Generated Message */
p {
background-color: #d1ecf1;
color: #0c5460;
padding: 10px;
border-radius: 4px;
text-align: center;
max-width: 500px;
margin: 20px auto;
}
</style>
<body>
<% if (locals.id) { %>
<p>Url generated: http://localhost:3000/url/<%= locals.id %></p>
<% } %>
<h1>Home Page</h1>
<form method="post" action="/url">
<label for="">Enter your original url</label>
<input type="text" name="url" placeholder="https://example.com">
<button type="submit">generate</button>
</form>
<div>
<% if(locals.urls) { %>
<table>
<thead>
<th>S. No</th>
<th>ShortID</th>
<th>Redirect</th>
<th>Clicks</th>
</thead>
<tbody>
<% urls.forEach((url, index) => { %>
<tr>
<td><%= index + 1 %></td>
<td><a href="http://localhost:3000/url/<%= url.shortID %>" target="_blank">http://localhost:3000/url/<%= url.shortID %></a></td>
<td><%= url.redirectURL %></td>
<td><%= url.totalclicks %></td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
</div>
</body>
</html>
7. Contributing
Contributions are welcome! Please follow these steps to contribute:
Fork the repository.
Create a new branch for your feature or bugfix.
Commit your changes and push to the branch.
Submit a pull request.