How to Design a URL Shortener: Architecture and Best Practices
To design a URL shortener, create a system that maps long URLs to short unique keys using a
hashing or base conversion method, store mappings in a database, and redirect users from short URLs to original URLs. Ensure scalability with caching, load balancing, and collision handling.Syntax
A URL shortener system typically involves these parts:
- Input: Long URL from user
- Shortening Logic: Generate a unique short key using hashing or base62 encoding
- Storage: Save mapping of short key to long URL in a database
- Redirect: When short URL is accessed, lookup original URL and redirect
This flow ensures users get a short link that redirects correctly.
javascript
function shortenURL(longURL) { // 1. Generate unique ID (e.g., auto-increment or hash) const id = generateUniqueId(longURL); // 2. Convert ID to base62 string for short key const shortKey = encodeBase62(id); // 3. Store mapping in database database.save(shortKey, longURL); // 4. Return short URL return `https://short.url/${shortKey}`; } function redirect(shortKey) { // Lookup original URL const longURL = database.get(shortKey); if (longURL) { redirectTo(longURL); } else { showError('URL not found'); } }
Example
This example shows a simple URL shortener using Node.js with an in-memory map and base62 encoding for keys.
javascript
const express = require('express'); const app = express(); app.use(express.json()); const urlDatabase = new Map(); let idCounter = 1; const base62Chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; function encodeBase62(num) { let str = ''; while (num > 0) { str = base62Chars[num % 62] + str; num = Math.floor(num / 62); } return str || '0'; } app.post('/shorten', (req, res) => { const { longURL } = req.body; if (!longURL) return res.status(400).send('Missing URL'); const shortKey = encodeBase62(idCounter++); urlDatabase.set(shortKey, longURL); res.send({ shortURL: `http://localhost:3000/${shortKey}` }); }); app.get('/:shortKey', (req, res) => { const longURL = urlDatabase.get(req.params.shortKey); if (longURL) { res.redirect(longURL); } else { res.status(404).send('URL not found'); } }); app.listen(3000, () => console.log('URL shortener running on port 3000'));
Output
POST /shorten with {"longURL":"https://example.com"} returns {"shortURL":"http://localhost:3000/1"}
GET /1 redirects to https://example.com
Common Pitfalls
Common mistakes when designing URL shorteners include:
- Collision: Not handling duplicate keys can cause wrong redirects.
- Scalability: Using a single database without caching or sharding can slow down lookups.
- Predictability: Sequential keys can expose usage patterns; use hashing or randomization.
- Expiration: Not handling expired or deleted URLs can cause errors.
Always validate URLs and handle errors gracefully.
javascript
/* Wrong: Using simple increment without collision check */ let id = 1; function shorten(url) { const key = id.toString(); id++; // No check if key already exists database[key] = url; return key; } /* Right: Use hash or check for existing keys */ function shortenSafe(url) { let key; do { key = generateRandomKey(); } while (database[key]); database[key] = url; return key; }
Quick Reference
- Use base62 encoding for short keys to keep URLs short and readable.
- Store mappings in a fast key-value store or database.
- Implement caching for frequent lookups to improve performance.
- Handle collisions by checking existing keys or using hashing with salt.
- Consider analytics and expiration policies for URLs.
Key Takeaways
Generate unique short keys using base62 encoding or hashing to map long URLs.
Store and retrieve URL mappings efficiently with a database and caching.
Handle key collisions and invalid URLs to avoid wrong redirects.
Design for scalability with load balancing, sharding, and caching layers.
Validate inputs and consider URL expiration or analytics features.