JAMStack URL Shortener with Netlify, 11ty and GitHub
Why...
I wanted a place where I can "bookmark" things I come across on the internet. Either for reading later, sharing or to keep a record of it for future reference.
For the sharing part I wanted to a way to use a shortened style link
Also, I want to own all this data. Along with not having to maintain a web application.
What if I could do all this with static site generation tool 11ty, and Netlify?
How...
Well turns out it's very possible, and much easier than you might think.
Broken down into the following parts
- Netlify to host the site and handle the URL redirection
- Eleventy to handle the website output and redirect file
- GitHub issues as a UI to add items
- GitHub actions to handle adding the content from the issue to the site
And the output of all this is my own URL shortner/bookmark "application" - https://roach.link
Netlfiy
For the Netlify part it's very straight forward. The provide a way to host a site, and also a mechanism for handling redirects. Setting up a site is pretty straight forward from their UI with a few customisable options. For the redirect part as long as you have a _redirects
file in the directory you want to serve your site from they take care of the rest. The also allow you have use a netlify.toml
file.
Eleventy (11ty)
For the 11ty part I went for a very similar set up to my personal site (this site) set up. Each link would be a markdown file inside a posts
directory that looks like this
---
title: "Matthew Roach"
date: "2021-01-24"
permalink: "b/view/"
link: "https://matthewroach.me"
tags: ["personal", "blog"]
---
Personal website of Matthew Roach
The frontmatter data is where the majority of the needed data is housed. You may notice the permalink
seems a little odd why is it b/view
? The b
part is the URL shortner unique ID, and the view part is used to build the output for each post to be roach.link/b/view
allowing you to view the individual bookmark item (single view page still work in progress).
Hang on.. You said unique ID... That's going to be fun to maintain I hear you ask. Well it would be if you were trying to do it by hand. But I am not, I am using an npm
package called bijective-link-shortener to increment them each time. Which is handled with GitHub as you'll see later.
Redirect file
Without the ability to do redirection a URL shortner isn't much use, and as previously mentioned Netlfiy allows you to do redirects if you provide either a _redirects
or netlify.toml
file in the root of the published site. And with 11ty this is made super simple to generate, as with 11ty you can configure it to output the input file to any output file (give or take), by setting the permalink
to be the output.
Given all the bookmarks are all going to be individual files in a folder I set up a new collection in the .eleventy.js
config file named allLinks
and reversed the collection so the newest would come first.
With a file named _redirects.11ty.js
in my src
directory I can consume the allLinks
collection and make 11ty render out the links in a format needed for Netlify. The _redirects.11ty.js
looks as follows
class Redirects {
data() {
return {
permalink: "netlify.toml",
};
}
render(data) {
return data.collections.allLinks
.map(
(l) =>
`
[[redirects]]
from = "${l.url.split("/")[1]}" // permalink is in format {shortLink}/view
to = "${l.data.link}"`
)
.join("");
}
}
module.exports = Redirects;
Broken down the file is looping over the allLinks
collection, and setting up the Netlify redirect file in the format they require by using the permalink data (first part) and the link from each posts frontmatter data. As as the permalink
of this file is set to netlify.toml
it will create that file based on the render
method.
The output file looks as follows
[[redirects]]
from = "b"
to = "https://matthewroach.me"
GitHub
GitHub is where I am hosted the source code and then have Netlify linked to the repo to build the site and publish on each commit to the main
branch.
Issues
But GitHub is also the primary way I add new data to the site. Rather than having to create a new file and fill in all the frontmater data, and do a host of git commands I wanted a way I could actually use this "application" with minimal effort. Turns out GitHub issues is a great perfectly acceptable way to be the UI for adding new bookmarks... That is if you are willing to make a small adjustment.
So, GitHub issues provides all but one of the interface items I needed. This is how I mapped the GitHub issue UI to the frontmatter data
- Issue Comment = The post body
- Labels = Tags
- Title = Title and link
Title = title and link? That seems odd. Well turns out the issue UI doesn't really have a field or place I could enter the URL for the item I want to bookmark. So I went with the format of making the title as follows:
Matthew Roach Blog::https://matthewroach.me
The two colons has no meaning other than for when the GitHub action runs and splits the title using title.split('::')
to parse it into two parts. The first part is the Title of the Site, and the second is then used in the link frontmatter data field.
Actions
GitHub actions is the glue that makes all the previous steps work together. Without actions the process of adding bookmarks would be very manual. Actions are a way for you to automate workflows from your GitHub repository. Based on an action you can configure an action to run and do "things". Those "things" are what makes this all possible.
As mentioned I am using GitHub issues as a UI to add new bookmarks. Broken down this works as follows
- Issue
- Add a new issue to my roach.link repository
- Once I am happy with the description and tags
- Mark the issue as closed
- GitHub action runs on issue closed event
- The action is triggered and starts its "steps"
- Action checks out code, sets up node and makes the issue object available on an environment variable
- Then a node command runs a script
node new-item-from-issue.js
1. Within the script I have access to the issue object by doingJSON.parse(process.env.ISSUE_CONTEXT)
2. Script checks how many posts are currently in theposts
folder, and using the packagebijective-link-shortener
gets the next short link value 3. Using the data from the issue object and the short link value from the previous step I usetransformAndWriteToFile
from the packagejson-to-frontmatter-markdown
to create a new markdown file in the format noted above - Add and commit is then run from within the action to add the newly created file to the repo
- Netlify is triggered due to a new commit on the
main
branch- Netlify runs its build steps I have configured
- New version of site is live at https://roach.link
- Homepage is updated
- New redirect added - example: https://roach.link/b