Make a URL shortener in Node.js!
Kacper Turon
Posted on February 14, 2023
Intro
I will show you how to create a simple and efficient basic URL shortener in Node.js with Express.js - it is a great personal project for learning with the bonus of being easily adaptable and expandable.
Full code: https://github.com/kacperturon/urlshortener
Prerequisites:
Create new Node project npm init -y
install Express.js npm i express
add to package.json to scripts a 'start' script start: "node index.js"
Optionally:
- Add eslint for linting:
npm i -D eslint
npx init eslint
- If using VS Code add extension https://tinyurl.com/vs-rest-client for testing purposes
1. Set up the server and 2 endpoints
app.post('/shorten', (req, res) => {
const { url, key } = req.body;
...
}
app.get('/:hash', (req, res) => {
const { hash } = req.params;
...
}
app.listen(port, () => console.log(`Server: URL shortener started on port ${port}`));
2. Create helpers
- Generate a hash of a specified length:
const getRandomHash = (len) => crypto.createHash('shake256', { outputLength: len }).update(crypto.randomBytes(20).toString('hex')).digest('hex');
- Calculate amount of possible combinations 26 [a-z] + 10 [0-9] ^ len + 1 (getRandomHash is in bytes so we add 1):
const possibleHashCombinations = (len) => (26 + 10) ** (len + 1);
3. Design data structure to keep track of the shortened URLs
const hashes = new Set();
const urlHashes = {};
Example data:
Set(5) { 'google', 'a3', '76', '60', 'cc' }
{ 'http://google.com': [ 'google', 'a3', '76', '60', 'cc' ],
'https://dev.to/: [ 'ge', 'dev' ] }
alternative approach: instead of having the key be the URL have the key be the hash, this will greatly impact our memory usage but also will greatly increase performance.
4. Design two endpoints
-
POST
/shorten
- shorten the received URL given a key or by generating a random hash
Generate hashes until one is unique or execution has timed out after 10s:
setTimeout(() => { halt = true; }, 1000 * 10);
do {
hash = getRandomHash(hashLength);
if (halt) return res.sendStatus(500);
} while (hashes.has(hash) || hash === null);
hashes.add(hash);
urlHashes[url] = urlHashes[url] ? [...urlHashes[url], hash] : [hash];
alternative approach: pre-generate hashes on server launch and immediately return the first free hash.
If all combinations are used up increase the hashLength
and recalculate:
combinationsAvailable -= 1;
if (combinationsAvailable === 0) {
hashLength += 1;
combinationsAvailable = possibleHashCombinations(hashLength);
}
return res.send(`${domain}/${hash}`);
-
GET
/:hash
- retrieve shortened URL and redirect
const url = Object.keys(urlHashes).find((u) => urlHashes[u].includes(hash));
return res.redirect(url);
5. Deal with edge-cases
/shorten
-
HTTP 403
- no URL provided -
HTTP 403
-key
provided already exists -
HTTP 403
-hash
orkey
already exists -
HTTP 500
- timed out while creating a uniquehash
/:hash
-
HTTP 404
- the URL for the hash does not exist
6. Test the endpoints
### POST with a specified key
POST http://localhost:3000/shorten
content-type: application/json
{
"url": "http://google.com",
"key": "google"
}
### POST without a key
POST http://localhost:3000/shorten
content-type: application/json
{
"url": "http://google.com"
}
### GET existing URL
GET http://localhost:3000/google
### GET non-existent URL
GET http://localhost:3000/googlenonexistent
7. Expandability ideas:
- Allow multiple URLs to be shortened at once
- Combine it with a Discord bot to pass users shortened URLs in chat
- On stopping the server create a backup of hashes and URLs and restore on launch
- Add full testing suite i.e. JEST or MOCHA
Conclusion
We went through the steps of creating a preliminary version of the URL shortener service, how to approach the design, split the work into endpoints and decide on a data structure, creating a great starting point for more specialised use cases.
Please let me know what you think of this article since this is my first one! Is it too simple? too short? valuable? good for new engineers?
What else would be interesting in the future:
- expanding this project?
- creating clones of i.e. Leetcode or Youtube or Twitter..
- focusing on actual Leetcode problems
- improvement ideas for programming React, microservices?
- system design work?
Any and all feedback is appreciated in these early stages of my article writing!
Posted on February 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.