Published on

System Design: Bit.ly

Authors
  • avatar
    Name
    Loi Tran
    Twitter

Introduction

1. Requirements

Functional Requirements

  • As a user I navigate I can enter a URL and get a shortened URL
  • As a user if I navigate to the shortened URL I'm redirected to the original URL I entered

Generate UIDs

// 1. Use Hashing(ensures same url gets same shortened URL)
const crypto = require('crypto')

function hashUrl(url, length = 7) {
  const hash = crypto.createHash('sha256').update(url).digest('base64url')
  return hash.slice(0, length)
}

// 2. Use Id
const urlToId = new Map()
const idToUrl = new Map()
let counter = 1000

const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
function toBase62(num) {
  let str = ''
  while (num > 0) {
    str = BASE62[num % 62] + str
    num = Math.floor(num / 62)
  }
  return str || '0'
}

function shortenUrl(url) {
  if (urlToId.has(url)) {
    return toBase62(urlToId.get(url))
  }

  const id = counter++
  urlToId.set(url, id)
  idToUrl.set(id, url)
  return toBase62(id)
}

function expandShortCode(shortCode) {
  const id = parseInt(shortCode, 62)
  return idToUrl.get(id)
}

Flip Key/Values

function flipSelective(obj) {
  const flipped = {}
  for (const [key, value] of Object.entries(obj)) {
    if (typeof value === 'number' || typeof value === 'string') {
      flipped[value] = key
    } else {
      flipped[key] = value
    }
  }
  return flipped
}

API Endpoint

const express = require('express')
const app = express()

const urlMap = {
  'uid-123': 'https://example.com/some-long-url',
  'uid-456': 'https://google.com',
}

app.get('/:uid', (req, res) => {
  const { uid } = req.params
  const redirectUrl = urlMap[uid]

  if (redirectUrl) {
    return res.redirect(302, redirectUrl)
  }

  res.status(404).send('URL not found')
})

const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})