Building a URL shortening service with NodeJS and MongoDB. Deploy to Azure. Front end Server Yayy we have come a long way!! NEXT STEP LAST STEP!!!

2025-06-07

使用 NodeJS 和 MongoDB 构建 URL 缩短服务。部署到 Azure。

前端

服务器

耶,我们已经走了很长一段路了!!

下一步

最后一步!!!

大家好,我们将使用 NodeJS、Express 和 MongoDB 构建一个 URL 缩短服务。然后,我们将把我们的 Web 应用程序部署到 Azure。这将是一个完整的代码教程,我会详细解释每一行代码。

最后将添加演示和 GitHub 存储库的链接。

很兴奋吧?
兴奋的

我应该知道/拥有什么

  • 对 HTML、CSS 和 Javascript 有基本的了解
  • 在您的计算机上安装 NodeJS(在此处安装)
  • 在您的计算机上安装 MongoDB(在此处安装)
  • 体验创建 GitHub 存储库,并将本地存储库推送到远程存储库。

让我们开始吧

首先,让我们为我们的应用程序创建一个文件夹。我们将其命名为 url-shortener。
Mkdir && cd

然后在终端运行npm init 这将为我们创建一个 package.json 文件。
npm 运行 init

现在让我们安装我们将要使用的软件包。express :Node.js框架,为 Web 和移动应用程序提供了一组强大的功能。
npm i
npm 我开发

body-parser:在处理程序之前解析传入的请求主体。

mongoose:Mongoose 是一个 MongoDB 对象建模工具,旨在在异步环境中工作。

nodemon:用于自动重启服务器,这样我们就不必在每次更改时都停止并重启服务器。我们将其安装为开发依赖项,因为我们只在开发过程中需要它。

安装完成后,编辑 package.json 的主要内容和脚本,如下所示。

{
  "name" : "url-shortener",
  "version" : "1.0.0",
  "description" : "URL shotener web app",
  "main" : "server.js",
  "scripts" : {
    "dev" : "nodemon server.js",
    "start" : "node server.js"
  },
  "keywords" : ["URL", "shortener"],
  "author" : "Your name",
  "dependencies" : {
    "express" : "^4.17.1",
    "mongoose" : "^5.9.7",
    "body-parser" : "^1.19.0"
  },
  "devDependencies" : {
    "nodemon" : "^2.0.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

前端

我们将使用一个非常基础的 UI。
对于应用程序的前端,请在工作目录中创建一个名为 public 的文件夹。我们将在这里存放前端文件(HTML、CSS 和 JavaScript)。在 public 文件夹中创建名为 index.html、style.css 和 main.js 的文件。index.html 和 style.css 的内容如下所示:

索引.html:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" 
              content="width=device-width, 
              initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="style.css">
        <title>URL shortener</title>
    </head>
    <body>
        <form id="url-form">
            <h1 class="header">URL SHORTENER</h1>
            <p class="desc">Shorten your long URL to 
             <span class="domain">mydomain.com</span>/unique_name
            </p>
            <p>
                <input required class="url-input" 
                id="original-url" type="url" 
                placeholder="paste original URL here">
            </p>
            <input disabled class="base-url" value="">
            <input required class="unique-input" id="unique-name" 
             type="text" placeholder="unique name">
            <p id='status'><button>SHORTEN</button></p>
            <p id="confirmationShow"></p>
        </form>
    </body>
    <script>
      const domain = window.location.host;
      document.querySelector('.domain').innerText = domain;
      document.querySelector('.base-url').value = domain;
    </script>
    <script src="main.js"></script>
</html>
Enter fullscreen mode Exit fullscreen mode

样式.css:

body{
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    background : linear-gradient(to right, #aa5f15, #542008);
}
html, body {
    font-family: Verdana, Geneva, Tahoma, sans-serif;
    overflow: hidden;
    height: 100%;
}
form{
    border: red;
    padding-top: 15vh
}
.a {
    color : white;
}
.header{
    color: bisque;
    letter-spacing: 3px;
    font-size: 3rem;
    margin-bottom: 1px;
}
.header span {
    font-style: italic;
}
.desc{
    margin-top :2px;
    color: bisque;
}
.base-url{
    padding: 10px;
    background-color: #a7a7a7;
    border-radius: 8px 0 0 8px;
    border: 1px solid black;
    width: 100px;
    font-weight: bold
}

.unique-input{
    padding: 10px;
    border-radius: 0 8px 8px 0;
    outline: none;
    border: 1px solid black;
}
.url-input{
    border-radius: 8px;
    padding: 10px;
    width: 300px;
    outline : none;
}

button{
    background-color: burlywood;
    padding: 10px;
    border-radius: 10px;
    outline: none;
    cursor: pointer;
}

#confirmationShow {
    font-style: italics;
}

.loader {
    border: 8px solid #f3f3f3;
    border-radius: 50%;
    border-top: 8px solid orange;
    width: 10px;
    height: 10px;
    -webkit-animation: spin 2s linear infinite;
    animation: spin 2s linear infinite;
    margin: 8px auto !important;
}

@-webkit-keyframes spin {
    0% { -webkit-transform: rotate(0deg); }
    100% { -webkit-transform: rotate(360deg); }
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
Enter fullscreen mode Exit fullscreen mode

服务器

在根目录下创建文件 server.js,并添加以下内容

服务器.js:

//Import modules
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');

//Call the express function to initiate an express app
const app = express();

//This tells express to parse incoming requests
app.use(bodyParser.json());

//This tells express we are serving static files (front end files)
app.use(express.static(path.join(__dirname, 'public')));

/** NB: process.env.PORT is required as you would 
not be able to set the port manually in production */
const PORT = process.env.PORT || 3000;

//app to listen to specified port
app.listen(PORT, () => {
  console.log(`Server running on port${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode
请注意,路径是内置节点模块,不需要安装

在终端中运行npm run dev
你应该会看到这个
终端结果

打开浏览器并访问http://localhost:3000。应该会显示出来。
前端

耶,我们的公共页面正在提供服务。

现在进入下一部分

让我们连接到我们的 MongoDB

在根目录中创建一个名为db.js的文件,并将其放入其中。

db.js:

//import mongoose library
const mongoose = require('mongoose');

//MONGO_URI 
const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/url'; 

//Connect to DB function
const connect = () => {
  mongoose.connect(MONGO_URI, {useNewUrlParser : true, useUnifiedTopology : true})
  .then(() => console.log('DB connected'))
  .catch(err => console.log(err));
  //On connection error, log the message
  mongoose.connection.on('error', err => {
    console.log(`DB connection error : ${err.message}`);
  });
}

//export the connect function, to use in server.js
module.exports = { connect }; 
Enter fullscreen mode Exit fullscreen mode

现在让我们回到我们的server.js,并实现与数据库的连接功能

服务器.js:

//Import modules
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');

//Import db module
const db = require('./db.js');

//Call the express function to initiate an express app
const app = express();

//Connect to database by calling our connect method
db.connect();

//This tells express to parse incoming requests
app.use(bodyParser.json());

//This tells express we are serving static files (front end files)
app.use(express.static(path.join(__dirname, 'public')));

/** NB: process.env.PORT is required as you would 
not be able to set the port manually in production */
const PORT = process.env.PORT || 3000;

//app to listen to specified port
app.listen(PORT, () => {
  console.log(`Server running on port${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

确保本地 Mongo 服务器正在运行。
服务器重启后,你应该会在终端中看到以下信息:
终端

创建 URL 模型

现在我们已经成功连接到数据库,让我们创建 URL 模型,它将保存我们想要在数据库中存储 URL 的格式。

创建一个名为 url.model.js 的文件,并将其放入。

url.model.js :

const mongoose = require('mongoose');

//create Url Schema (format)
const urlSchema = new mongoose.Schema({
    originalUrl: {
        type : String,
        required : true
    },
    shortUrl : {
        type : String,
        required : true
    },
    unique_name : {
        type : String,
        required : true
    },
    dateCreated : {
        type : Date,
        default : Date.now
    }  
});
//Use schema to create a Url model
const Url = mongoose.model('Url', urlSchema);

//Export Url Model
module.exports = Url;
Enter fullscreen mode Exit fullscreen mode

创建控制器来处理所有路由

我们现在将创建控制器来处理我们的两个路线:

  • 创建短链接
  • openShortLink 创建一个名为 url.controllers.js 的文件并添加以下代码:

url.controller.js :

//import Url model
const Url = require('./url.model.js');

//This is basically your domain name
const baseUrl = process.env.BASE_URL || 'http://localhost:3000';

const createShortLink = async (req, res) => {
    //get the originalUrl and unique_name from the request's body
    let { originalUrl, unique_name } = req.body;

    try {
        //check if unique_name alredy exists
        let nameExists = await Url.findOne({ unique_name });
        /** if unique_name already exists, send a response with an
        error message, else save the new unique_name and originalUrl */
        if(nameExists){
            return res.status(403).json({
                error: "Unique name already exists, choose another",
                ok : false
            }) 
        }
        else {
            const shortUrl = baseUrl + '/' + unique_name;
            url = new Url({
                originalUrl,
                shortUrl,
                unique_name
            });
            //save
            const saved = await url.save();
            //return success message shortUrl
            return res.json({
                message : 'success',
                ok : true,
                shortUrl
            });
        }
    } catch (error) {
        ///catch any error, and return server error
        return res.status(500).json({ok : false, error : 'Server error'});
    }
};

const openShortLink = async (req, res) => {
    //get the unique name from the req params (e.g olamide from shorten.me/olamide)
    const { unique_name } = req.params;

    try{
      //find the Url model that has that unique_name
      let url = await Url.findOne({ unique_name });

       /** if such Url exists, redirect the user to the originalUrl 
       of that Url Model, else send a 404 Not Found Response */
        if(url){
            return res.redirect(url.originalUrl);
        } else {
            return res.status(404).json({error : 'Not found'});
        }  
    } catch(err) {
       //catch any error, and return server error to user
        console.log(err);
        res.status(500).json({error : 'Server error'});
    } 
};

module.exports = {
    createShortLink, openShortLink
}
Enter fullscreen mode Exit fullscreen mode

配置路线

让我们回到 server.js 并在路由中使用刚刚创建的控制器。
首先,我们需要导入它们并按如下所示使用。

服务器.js:

//Import modules
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');

//Import db module
const db = require('./db.js');

//Import controllers
const { createShortLink, openShortLink } = require('./url.controller.js');

//Call the express function to initiate an express app
const app = express();

//Connect to database by calling our connect method
db.connect();

//This tells express to parse incoming requests
app.use(bodyParser.json());

//This tells express we are serving static files (front end files)
app.use(express.static(path.join(__dirname, 'public')));

//USE CONTROLLERS
//route to create short link
app.post('/createShortLink', createShortLink);
//route to open short link, ':' means unique_name is a param
app.get('/:unique_name', openShortLink);

/** NB: process.env.PORT is required as you would 
not be able to set the port manually in production */
const PORT = process.env.PORT || 3000;

//app to listen to specified port
app.listen(PORT, () => {
  console.log(`Server running on port${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

耶,我们已经走了很长一段路了!!

现在让我们开始从前端发出请求。

打开 public/main.js 文件并添加以下内容:

main.js :

const urlForm = document.getElementById('url-form');
const originalUrl = document.getElementById('original-url');
const uniqueName = document.getElementById('unique-name');
const confirmationShow = document.getElementById('confirmationShow');
const status = document.getElementById('status');

const formSubmit = e => {
    e.preventDefault();
    status.innerHTML = '<button type="button" class="loader"></button>'
    fetch('/createShortLink', {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          originalUrl : originalUrl.value,
          unique_name : uniqueName.value
        })
    })
    .then(data => data.json())
    .then(response => {
        status.innerHTML = '<button>SHORTEN</button>'
        if(!response.ok){
            confirmationShow.innerText = response.error;
        }
        else {
            confirmationShow.innerHTML = `Hooray!!! The link can now be visited 
            through <a target="_blank" 
            href=${response.shortUrl} rel = "noopener noreferer" > 
            ${response.shortUrl} </a>`;
        }
    })
    .catch(err => {
        console.log('oops', err);
        status.innerHTML = '<button>SHORTEN</button>';
        confirmationShow.innerText = 'Network error, retry'
    })
};

urlForm.addEventListener('submit', formSubmit);
Enter fullscreen mode Exit fullscreen mode

就是这样 !!!

现在确保你的服务器正在运行,打开浏览器,访问http://localhost:3000。在原始 URL 字段中输入一个长 URL,并在唯一名称字段中输入一个唯一名称。提交表单,看看奇迹是否发生了。

受赠人

下一步

GitHub 存储库

为项目创建一个 GitHub 存储库,并将项目推送到远程存储库(按照本指南操作

MongoDB 服务器

在将项目部署到 Azure 之前,我们需要一个远程 MongoDB 服务器,因为 Azure 无法连接到我们本地服务器上的数据库。前往MongoDB Atlas获取连接字符串。这将是服务器上的 MONGO_URI 变量。(还记得我们之前在应用中添加了 process.env.MONGO_URI 吗?)您可以按照本指南获取连接字符串。

最后一步!!!

部署到 Azure

  • 前往Azure 门户创建帐户。注意:注册 Azure 后,您将获得 200 美元的 Azure 试用额度,试用期为 30 天。需要信用卡验证。如果您是学生,请点击此处免费创建帐户,无需信用卡。

谢谢

就这样!我们的应用上线啦!!!

转到您的网站的 URL 并进行测试。

按照本指南为该应用购买并设置一个实际的短自定义域名。我购买的是rdre.me。

您可以继续向您的应用添加更多功能,例如在用户创建短链接之前对其进行注册、设置短链接到期日期等。

感谢您来到这里。

谢谢

演示链接:https://rdre.me

GitHub 存储库链接:https://github.com/aolamide/shorten-url

请留下您的评论和问题。

您可以通过LinkedInTwitter联系我。

文章来源:https://dev.to/olamideaboyeji/building-a-url-shortening-service-with-nodejs-and-mongobb-deploy-to-azure-oep
PREV
配置 nginx 来托管多个子域名
NEXT
🎉像专业人士一样监控你的 Javascript 应用程序🧙‍♂️💫