Week # 10: DevOps Learning Journey - Building a URL Shortener with Node.js, MongoDB, and EJS(Deploy on AWS)

Week # 10: DevOps Learning Journey - Building a URL Shortener with Node.js, MongoDB, and EJS(Deploy on AWS)

In this blog, we’ll dive into the process of creating a URL shortener using Node.js, Express, MongoDB, and EJS. We’ll explain the workflow, demonstrate key pieces of code, and guide you through the setup process.


Features of the URL Shortener


Tools and Technologies

Here’s the tech stack and packages we used:

  1. Node.js: For building the server-side logic.

  2. Express.js: To manage routes and middleware.

  3. MongoDB: For database storage of URLs and analytics.

  4. EJS: As the templating engine for rendering dynamic HTML.

  5. shortid/nanoid: For generating unique short IDs for URLs.


Project Setup and Workflow

1. Initializing the Project

Start by creating a Node.js project and installing the required dependencies:

npm init -y  
npm install express mongoose ejs nanoid shortid

2. Folder Structure

The project follows an MVC architecture:

plaintextCopy codeproject/
├── controllers/
│   └── url.js
├── models/
│   └── url.js
├── routes/
│   ├── url.js
│   └── staticRouter.js
├── views/
│   ├── home.ejs
│   └── analytics.ejs
├── index.js
├── connect.js
└── package.json

Code Walkthrough

Database Connection (connect.js)

Connect to the MongoDB cluster:

const mongoose = require('mongoose');

async function connectToMongoDB(uri) {
    return mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
}

module.exports = { connectToMongoDB };

URL Schema and Model (models/url.js)

Defines the structure for storing URLs:

const mongoose = require('mongoose');

const urlSchema = new mongoose.Schema({
    shortId: { type: String, required: true, unique: true },
    redirectURL: { type: String, required: true },
    visitHistory: [{ timeStamp: { type: Number } }],
}, { timestamps: true });

const URL = mongoose.model("url", urlSchema);
module.exports = URL;


Controller Logic (controllers/url.js)

Handles the core functionalities:

  1. Generate Short URL

     const shortid = require('shortid');
     const URL = require('../models/url');
    
     async function handleGenrateNewShortURL(req, res) {
         const body = req.body;
         if (!body.url) return res.status(400).json({ error: 'URL is required' });
    
         const shortID = shortid.generate();
         await URL.create({ shortId: shortID, redirectURL: body.url, visitHistory: [] });
    
         return res.render('home', { id: shortID });
     }
    
  2. Redirect and Track Visits

     async function handleGetAnalytics(req, res) {
         const shortId = req.params.shortId;
         const result = await URL.findOne({ shortId });
    
         return res.json({
             totalClicks: result.visitHistory.length,
             analytics: result.visitHistory
         });
     }
    
     module.exports = {
         handleGenrateNewShortURL,
         handleGetAnalytics,
     };
    

Routes (routes/url.js)

Defines API endpoints for URL generation and analytics:

const express = require('express');
const { handleGenrateNewShortURL, handleGetAnalytics } = require('../controllers/url');
const router = express.Router();

router.post("/", handleGenrateNewShortURL);
router.get("/analytics/:shortId", handleGetAnalytics);

module.exports = router;

Main Server File (index.js)

Sets up the Express application and configures routes:

const express = require('express');
const app = express();
const path = require('path');
const { connectToMongoDB } = require('./connect');

const urlRoute = require('./routes/url');

app.set("view engine", "ejs");
app.set("views", path.resolve("./views"));

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

connectToMongoDB("YOUR_MONGO_URI").then(() => {
    console.log("Connected to MongoDB");
});

app.use("/url", urlRoute);

app.listen(8001, () => {
    console.log("Server running on PORT 8001");
});

Dynamic Templates with EJS

  1. Home Template (views/home.ejs):
    Displays the short URL:

     <h1>Your Shortened URL</h1>
     <p><a href="/url/<%= id %>">localhost:8001/url/<%= id %></a></p>
    
  2. Analytics Page (views/analytics.ejs):
    Shows analytics data.


Workflow Explanation

  1. User Input: The user submits a URL through a form.

  2. Short URL Creation:
    Th
    e server generates a unique short ID using shortid and stores it in the database with the original URL.

  3. Redirection:
    When a user accesses the short URL, the server redirects them to the original URL and logs the visit.

  4. Analytics:
    A separate endpoint allows users to view analytics like the number of clicks and timestamps.


Deploy and Test

  1. Start the application locally:

     nodemon index.js
    
  2. Access it at http://localhost:8001.


This project demonstrates fundamental concepts of web development, MVC architecture, and database integration. Modify and scale it to suit your needs!