How to make a URL shortner.
Tags: javascript, webdev, mongodb, api
Published: 2022-07-22 09:04:00

In this project you will learn how to build an API using express.js, how to connect it to a backend and how to handle http requests

fork or clone this [repository](https://github.com/prajyu/linkshrink.git) for reference
 

## NPM packages used
- dependencies

```
cors
crc
express
mongodb
url
```
 
- dev dependencies  ( _npm i ${package_name}--save-dev_ )
```
dotenv
nodemon
```
 

## Initialize project
Initialize an npm project `npm init -y` and create **api.js** file
 


## Build the Server
  
**Install and import dependencies**

  1. Install all the dependencies given above. dev depedencies is optional.

        - **nodemon** is used to automatically update the server file aka _api.js_
      - **dotenv** is used to load environment variables into the file
  
2. Import dependencies into api.js

```js
const express = require("express");
const cors = require("cors");
const { crc32 } = require("crc");
const { MongoClient } = require("mongodb");
const { URL } = require("url");
```
 
** Start the server **

1. Make the express app instance using this line of code
```js
const app = express();
```
 
2. Make the server able to recieve json files on **POST** requests

```js
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
```
 
3. Enable CORS on the server. This helps you in controlling access to the server from different addresses more about it [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

```js
app.use(cors());
```
 
4. Make the server listen on a port
```js
app.listen(process.env.PORT || 3030); 
```
`process.env.PORT` helps in deployment

**Now you have a working server 🎉🎉. But it doesn't do anything right now. So lets fix that...**
 
## Connect your server to mongoDB

If you don't know how to get started mongodb locally. Here's a [link](https://zellwk.com/blog/local-mongodb/) to get you started 

- We use npm package **monodb** to connect to database

```js
const dbName = "dbName";

let collection = null // Global Variable

let main = async () => {
  let url = "mongodb://localhost:27017";
  const client = new MongoClient(url);
  await client.connect();
  const db = client.db(dbName);
  collection = db.collection("urls");
  const deleteResult = await collection.deleteMany({});
  // deletes all entry after each restart
};
```

And call the function **`main()`**

Now your server is connected to your databse.
 
## Link Shortner Function
We are going to be using CRC32 hash algorithm to shrink the URLS. This is where crc package comes into play.

```js
let createUrl = (url) => {
  let shortenedUrl = crc32(url).toString(16);
  return shortenedUrl;
};
```

This function reduces your URL to a 8 character string.
 
## Create and Read from database

We are going to be making a collection with a pair like

`{
  url:'original URL,
  shortenedUrl:'shrinked URL of the absolute URL
}`
 
- Add the URL to databse
```js
let addUrl = async (url, shortenedUrl) => {
  const insertResult = await collection.insertOne({ shortenedUrl, url });
  return insertResult;
};
```
 
- Find pair from databse
```js
let findUrl = async (shortenedUrl) => {
  const findResult = await collection.find({ shortenedUrl }).toArray();
  return findResult;
};
```
 
## Add routes to your API
 
- **Shrink URL**

To shrink URL's we need to create a POST request in api.js

```js
app.post("/shrink", async (req, res) => {
  ...
});
```
After we defined the route. We need to process the incoming requests.

Get the URL from the body of the post request and shrink it using our earlier function.
 
```js
let { url } = req.body;
let shortenedUrl = createUrl(url);
```
 
Next we check if the shortenedUrl is already in our database using **findUrl()** function. If we get a hit we return the shortenedUrl or move to next part.
```js
let result = await findUrl(shortenedUrl);
if (result.length >= 1) return res.json({ shortenedUrl });
```
 
If the shortenedUrl doesn't exist we add it to the database with the structure defined earlier above and send back the shortenedUrl.

```js
let status = await addUrl(url, shortenedUrl);
if (status?.acknowledged) return res.json({ shortenedUrl });
else return res.json({ error: true }); // Not to make the server crash
```
 
**'/shrink' route in one snippet**
```js
app.post("/shrink", async (req, res) => {
  let { url } = req.body;
  let shortenedUrl = createUrl(url);
  let result = await findUrl(shortenedUrl);
  if (result.length >= 1) return res.json({ shortenedUrl });
  let status = await addUrl(url, shortenedUrl);
  if (status?.acknowledged) return res.json({ shortenedUrl });
  else return res.json({ error: true });
});
```
- **Strecth URL**

To stretch URL we are going to define a **GET** route

First we define the route in api.js

```js
app.get("/s/:shortUrl", async (req, res) => {
  ...
});
```
 
The **:shortUrl** in the route section capsules all the section after **'/s/'** into a variable.

First we check if the url is in the database or not. If it's in the database we redirect the user to the URL or we send a 404 message

```js
let { shortUrl } = req.params;
let result = await findUrl(shortUrl);
if (result.length > 0) return res.redirect(result[0]?.url);
else res.json({ 404: "no URL found" });
```
 
**'/s/:shortUrl' route in one snippet**
```js
app.get("/s/:shortUrl", async (req, res) => {
  let { shortUrl } = req.params;
  let result = await findUrl(shortUrl);
  if (result.length > 0) return res.redirect(result[0]?.url);
  else res.json({ 404: "no URL found" });
});
```

---

**And voila you have a working link shortner 🎉🎉🎊**
**Here's a working link shortner [shrinkinglinks](https://shrinkinglinks.web.app/)**