System Design

Case Study: URL Shortener

Requirements

Functional

Non-Functional

API Design

POST /api/shorten
{
  "url": "https://example.com/very/long/url",
  "customAlias": "my-link" // optional
}

Response:
{
  "shortUrl": "https://short.ly/abc123"
}

GET /{shortCode} → Redirect 301

Database Schema

CREATE TABLE urls (
  id BIGSERIAL PRIMARY KEY,
  short_code VARCHAR(10) UNIQUE,
  original_url TEXT,
  created_at TIMESTAMP,
  expires_at TIMESTAMP,
  user_id BIGINT,
  click_count BIGINT DEFAULT 0
);

CREATE INDEX idx_short_code ON urls(short_code);

Short Code Generation

Base62 Encoding

const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

function encode(num) {
  let result = '';
  while (num > 0) {
    result = ALPHABET[num % 62] + result;
    num = Math.floor(num / 62);
  }
  return result || '0';
}

// ID 12345 → short code "3D7"
// 7 characters = 62^7 = 3.5 trillion URLs

Architecture

User Request

CDN (cache popular links)

Load Balancer

API Servers (stateless)

Redis Cache (hot URLs)

PostgreSQL (master-slave)

Caching Strategy

async function redirect(shortCode) {
  // L1: Check cache
  let url = await redis.get(`url:${shortCode}`);
  
  if (!url) {
    // L2: Database
    url = await db.query('SELECT original_url FROM urls WHERE short_code = ?', [shortCode]);
    
    // Cache for 24h
    await redis.set(`url:${shortCode}`, url, { EX: 86400 });
  }
  
  // Async analytics
  queue.publish('click', { shortCode });
  
  return url;
}

Scaling

100M URLs/month:

10B redirects/month:

Keyingi dars: Twitter loyihalash.