# Next.js Shortened URL Automation

> Automate the process of creating shortened URLs for your Next.js posts with this how-to guide.

- Canonical: https://dennisokeeffe.com/blog/2021-11-04-nextjs-shortened-url-automation
- Published: 2021-11-04
- Updated: 2021-11-04
- Tags: nextjs, javascript

This post will demonstrate how to create a shortened URL for your Next.js website.

It will follow a similar process that I am using for shortened URLs on my [personal blog website](https://blog.dennisokeeffe.com), although this example will be in JavaScript as opposed to TypeScript (which my blog is written in).

The final code can be found on my [GitHub repo "okeeffed/hello-nextjs-url-shortener"](https://github.com/okeeffed/hello-nextjs-url-shortener).

## Prerequisites

1. Basic familiarity with [Create Next App](https://nextjs.org/docs/api-reference/create-next-app).
2. `create-next-app` installed.
3. Basic familiarity with [Next.js](https://nextjs.org/).

## Getting started

We will let `create-next-app` create the project directory `hello-url-shortener` for us:

```s
$ npx create-next-app hello-url-shortener
# ... creates Next.js app for us
$ cd hello-url-shortener
```

At this stage, a working Next.js app is ready for us.

Let's create a couple more folders and files to host our script.

```s
# Create a content folder to host markdown files
$ mkdir scripts
$ touch scripts/urlShortener.js

# Create a folder for some pages - we will be verbose since this is more a demonstration
$ mkdir -p pages/content
$ touch pages/content/pageA.js pages/content/pageB.js
```

We will need to install the following dependencies:

```s
npm i --save-dev md5 recursive-readdir
```

We will use `md5` to generate hashes and `recursive-readdir` to read the contents of the `/pages/content` directory.

> Note: this example will demonstrate shortening for two verbose URLs, but you should tweak the script to generate the relevant output to your project. If you are using dynamic content (like I am for my blog), then you need to ensure that you are referencing the data for those paths to know what paths you will need to shorten in the script.

## Setting up our pages

To keep things simple, add the following to `pages/content/pageA.js` to `pages/content/pageB.js` and `pages/content/pageB.js` respectively:

```js
// pages/content/pageA.js
export default function PageA() {
  return <div>PageA, woo!</div>;
}

// pages/content/pageB.js
export default function PageB() {
  return <div>PageB, woo!</div>;
}
```

We are going to keep things simple, as the important part is the redirect.

If you run `next dev` in the terminal, you should see the following in the browser for `PageA` and `PageB` at `localhost:3000/content/pageA` and `localhost:3000/content/pageB` respectively:

![Page A on localhost](https://cdn.dennisokeeffe.com/assets/2021-11-04-nextjs-shortened-url-automation/1-page-a.webp)

![Page B on localhost](https://cdn.dennisokeeffe.com/assets/2021-11-04-nextjs-shortened-url-automation/2-page-b.webp)

At this stage, we are now ready to create our shortened URL path.

## Writing our redirects script

Next.js docs have information on [performing redirects](https://nextjs.org/docs/api-reference/next.config.js/redirects) in the `next.config.js` file.

A redirect looks like the following:

```js
module.exports = {
  async redirects() {
    return [
      {
        source: "/about",
        destination: "/",
        permanent: true,
      },
    ];
  },
};
```

What we want to do is write a script that will manage the return value of the `redirects` function for us.

First, create the `next.config.js` file using `touch next.config.js` and add the following:

```js
const urlPaths = require("./public/urls.json");

module.exports = {
  // ... omitted anything else that may be in this file
  async redirects() {
    return urlPaths.redirects;
  },
};
```

Now that our config file is setup, we can add the following to our `scripts/urlShortener.js` file:

```js
const { readFileSync, writeFileSync, existsSync } = require("fs");
const recursive = require("recursive-readdir");
const md5 = require("md5");
const path = require("path");

async function main() {
  const contentPagePath = path.resolve(__dirname, "../pages/content");
  const urlShortenerFilePath = path.resolve(__dirname, "../public/urls.json");
  const urlShortenerFileExists = existsSync(urlShortenerFilePath);

  const files = await recursive(contentPagePath, ["!*.js"]);
  const currentUrls = urlShortenerFileExists
    ? JSON.parse(readFileSync(urlShortenerFilePath, "utf-8"))
    : { redirects: [] };

  // Find all current hashes in the public urls JSON file
  const currentEntries = currentUrls.redirects.map((url) => url.destination);
  const currentHashes = currentUrls.redirects.map((url) => url.source);

  for (const file of files) {
    const filePath = file
      // Replace the prefix
      .replace(`${process.cwd()}/pages`, "")
      // Replace file JavaScript suffix
      .replace(".js", "");

    // Check if we already have a hash for our file
    if (currentEntries.includes(filePath)) {
      continue;
    }

    // Create a hash for our file
    let outputHash = `/${md5(file).slice(0, 4)}`;

    // If our hash collides, rehash
    while (currentHashes.includes(outputHash)) {
      outputHash = `/${md5(outputHash).slice(0, 4)}`;
    }

    currentUrls.redirects.push({
      source: outputHash,
      destination: filePath,
      permanent: true,
    });
  }

  writeFileSync(
    urlShortenerFilePath,
    JSON.stringify(currentUrls, null, 2),
    "utf8"
  );
}

main();
```

The script above does the following:

1. Assigning some sensible variable defaults after resolving where the files sit.
2. Using `recursive-readdir` to fetch all the `.js` files in the `pages/content` directory.
3. Checks if our `public` folder has a file called `urls.json` and assigns the value to `currentUrls`. If not, we are assigning `currentUrls` to a default object with empty redirects.
4. Iterating through all of those files file (for us, `pageA.js` and `pageB.js`) and generating a hash for each if an entry does not already exist.
5. Each hash is assigned to our `currentUrls.redirects` array as an object to represents what we saw in the Next.js docs.
6. Writing that file back out to `public/urls.json`.

All that is left to do is run that script. In the terminal, run `node scripts/urlShortener.js` and you should see the output in `public/urls.json`:

```json
{
  "redirects": [
    {
      "source": "/d475",
      "destination": "/content/pageA",
      "permanent": true
    },
    {
      "source": "/42e2",
      "destination": "/content/pageB",
      "permanent": true
    }
  ]
}
```

Awesome! So the path `/d475` should now redirect to `/content/pageA` (and so on for the rest).

To have the `next.config.js` input take effect, you will need to restart `next dev`.

Once, that is done, head to `localhost:3000/{source}` and you should see the page redirect to the `destination` (where `source` is a value from your `public/urls.json` directory).

Success!

## What next?

The URL shortener script that we wrote will only apply to shortening the `path` part of the URL. If you want to have a shorter page like `bit.ly` etc, you will need to buy the domain for that.

For my personal links, I purchased the domain `dnok.li` (thinking it was short for **Dennis O'Keeffe Link**). You will need to apply that to your website, like how I did for my blog (which is being hosted on Vercel):

![Applying my domain name](https://cdn.dennisokeeffe.com/assets/2021-11-04-nextjs-shortened-url-automation/3-domain-setup.webp)

Once that is done, then that in combination with the redirects generator we bring the goods (such as the shortened link for this blog post [`https://dnok.li/b7c2`](https://dnok.li/b7c2)).

## Summary

Today's post demonstrated how to create a URL shortener for your Next.js website programmatically.

This can be incredibly useful for any URLs taking up precious space on your shared content.

## Resources and further reading

1. Basic familiarity with [Create Next App](https://nextjs.org/docs/api-reference/create-next-app).
2. Basic familiarity with [Next.js](https://nextjs.org/).
3. [Final code](https://github.com/okeeffed/hello-nextjs-url-shortener).

_Photo credit: [`pawel_czerwinski`](https://unsplash.com/@pawel_czerwinski)_
