Post request to /short-url Body:{url:"longurl1"}
returns:{shorturl}
So this would first validate the URL if it is correct or not so if correct then would proceed to back end then the bacon would have shot in url functionality then it was saving the database and return the URL.
Get: /short-url will fetch all the url which user has it and will alow params based and paginated
Post: /long-url Body:{url:shorturl} will check the db if this short url exist in our place and if found return the orignal url
When user hits the system:
POST /shorten
Options:
Example:
https://example.com/abc → "xYz123"
shortCode → longURL
cache[xYz123] = longURL
https://short.ly/xYz123
GET /xYz123
if exists → return immediately
xYz123 → longURL
HTTP 302 → original URL
short_code (PRIMARY KEY)
long_url
created_at
expiry (optional)
Key points:
short_code
User → API → Cache → DB (if needed)
Let’s make it solid:
| ComponentResponsibility | |
| API Layer | Validation, auth, rate limiting |
| Write Service | Generate short URL, store |
| Read Service | Handle redirects |
| Cache (Redis) | Fast lookup |
| DB | Persistent storage |
Here is a complete, production-ready system design for a TinyURL-like URL shortener, with all critical details included end-to-end.
User → Load Balancer → App Servers
Write Path:
User → API → Identifier Service → Storage → Cache → Response
Read Path:
User → API → Cache → DB → Redirect
Generate globally unique, scalable, collision-safe IDs
| timestamp (41) | machine_id (10) | sequence (12) |
machine_id
UNIQUE(short_code)
Generate → Insert
↓
Conflict → regenerate → retry (max 2–3 times)
Pros
Cons
Handle:
GET /abc123 → Redirect to original URL
short_codeUse Redis
Pros
Cons
Store mapping:
short_code → long_url
Table: url_mapping
short_code (PK)
long_url
created_at
expiry_time
user_id (optional)
short_codeuser_id (optional)
hash(short_code) % N
100M × 200B = 20GB
index overhead → ~40–50GB
| OptionProsCons | ||
| SQL | Strong consistency | Harder to scale |
| NoSQL | Scalable | Eventual consistency |
Use Redis
TTL = expiry_time − current_time
short_code → NULL
TTL = 5 minutes
DB update → Cache delete/update
URL_UPDATED / URL_DELETED
Capacity: 100 requests
Refill: 10 req/sec
Use Redis
rate_limit:{user_id}
429 Too Many Requests
Retry-After: 10
Exponential backoff:
1s → 2s → 4s → 8s → capped
| ApproachProsCons | ||
| Token Bucket | Handles bursts well | Slight complexity |
| Fixed Window | Simple | Burst spikes |
| Sliding Window | Accurate | Expensive |
User → API
→ Generate ID
→ Base62 encode
→ Store in DB
→ Update cache
→ Return short URL
User → API
→ Cache lookup
→ HIT → Redirect
→ MISS → DB → Cache → Redirect