Skip to content

Instantly share code, notes, and snippets.

@ngshiheng
Last active January 13, 2022 14:07
Show Gist options
  • Save ngshiheng/358f1ff3c0ed184a19ab5f8561c529d0 to your computer and use it in GitHub Desktop.
Save ngshiheng/358f1ff3c0ed184a19ab5f8561c529d0 to your computer and use it in GitHub Desktop.
I Built My Own TinyURL. Here’s How I Did it https://jerrynsh.com/i-built-my-own-tiny-url/
// handlers/createShortUrl.js
import { generateUniqueUrlKey } from '../utils/urlKey'
export const createShortUrl = async (request, event) => {
try {
const urlKey = await generateUniqueUrlKey()
const { host } = new URL(request.url)
const shortUrl = `https://${host}/${urlKey}`
const { originalUrl } = await request.json()
const response = new Response(
JSON.stringify({
urlKey,
shortUrl,
originalUrl,
}),
{ headers: { 'Content-Type': 'application/json' } }
)
event.waitUntil(URL_DB.put(urlKey, originalUrl))
return response
} catch (error) {
console.error(error, error.stack)
return new Response('Unexpected Error', { status: 500 })
}
}
// handlers/createShortUrl.js
import { URL_CACHE } from '../utils/constants'
import { generateUniqueUrlKey } from '../utils/urlKey'
export const createShortUrl = async (request, event) => {
try {
const urlKey = await generateUniqueUrlKey()
const { host } = new URL(request.url)
const shortUrl = `https://${host}/${urlKey}`
const { originalUrl } = await request.json()
const response = new Response(
JSON.stringify({
urlKey,
shortUrl,
originalUrl,
}),
{ headers: { 'Content-Type': 'application/json' } }
)
const cache = await caches.open(URL_CACHE) // Access our API cache instance
event.waitUntil(URL_DB.put(urlKey, originalUrl))
event.waitUntil(cache.put(originalUrl, response.clone())) // Update our cache here
return response
} catch (error) {
console.error(error, error.stack)
return new Response('Unexpected Error', { status: 500 })
}
}
// index.js
import { Router } from 'itty-router'
import { createShortUrl } from './src/handlers/createShortUrl'
import { redirectShortUrl } from './src/handlers/redirectShortUrl'
import { LANDING_PAGE_HTML } from './src/utils/constants'
const router = Router()
// GET landing page html
router.get('/', () => {
return new Response(LANDING_PAGE_HTML, {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
})
})
// GET redirects short URL to its original URL.
router.get('/:text', redirectShortUrl)
// POST creates a short URL that is associated with its an original URL.
router.post('/api/url', createShortUrl)
// 404 for everything else.
router.all('*', () => new Response('Not Found', { status: 404 }))
// All incoming requests are passed to the router where your routes are called and the response is sent.
addEventListener('fetch', (e) => {
e.respondWith(router.handle(e.request))
})
// handlers/redirectShortUrl.js
export const redirectShortUrl = async ({ params }) => {
const urlKey = decodeURIComponent(params.text)
const originalUrl = await URL_DB.get(urlKey)
if (originalUrl) {
return Response.redirect(originalUrl, 301)
}
return new Response('Invalid Short URL', { status: 404 })
}
import { URL_CACHE } from '../utils/constants'
export const shortUrlCacheMiddleware = async (request) => {
const { originalUrl } = await request.clone().json()
if (!originalUrl) {
return new Response('Invalid Request Body', {
status: 400,
})
}
const cache = await caches.open(URL_CACHE)
const response = await cache.match(originalUrl)
if (response) {
console.log('Serving response from cache.')
return response
}
}
// utils/urlKey.js
import { customAlphabet } from 'nanoid'
const ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
/*
Generate a unique `urlKey` using `nanoid` package.
Keep retrying until a unique urlKey which does not exist in the URL_DB.
*/
export const generateUniqueUrlKey = async () => {
const nanoId = customAlphabet(ALPHABET, 8)
let urlKey = nanoId()
while ((await URL_DB.get(urlKey)) !== null) {
urlKey = nanoId()
}
return urlKey
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment