September 29, 2026
Expiring URL service — designing a small API around explicit failure modes
Built an Express URL shortener with eight-character Nano ID aliases, ephemeral Map storage, redirects, JSON lookup, timer-based expiry, and documented scale-up design.
Personal prototype
- Role
- Software engineer
- Published
- September 2026
- Focus
- Personal prototype
- Engineer
- Saaim Abdullah

Small products still require real systems thinking
Map stores aliases within one process; Nano ID generates short keys; and a timer removes each record at expiry. Its value is a complete request lifecycle with transparent trade-offs—not an invented claim of millions of requests per second.
Numbers that are actually grounded
| Metric | Implementation | Why it matters |
|---|---|---|
| Alias length | 8 characters | Short links with a defined identifier size |
| Primary URL actions | 3 — create, redirect, fetch original | End-to-end link lifecycle and inspection |
| Additional operational route | 1 health endpoint | Basic process liveness check |
| Persistence stores | 1 in-process Map | Constant-time-style lookup without network database overhead |
| Expiration scheduling | 1 timer per created link | Simple lifecycle behavior, bounded by process lifetime |
| Process durability | 0 after restart | Links do not survive a fresh process |
Request lifecycle, step by step
| Action | Client request | Server behavior | Response / outcome |
|---|---|---|---|
| Create | POST original destination to /api/url | Generate 8-character ID and store mapping | Return short URL based on request host |
| Redirect | GET the short-ID route | Find destination in Map | Redirect if present; otherwise 404 |
| Inspect | GET fetch endpoint for ID | Look up original destination | JSON result or 404 |
| Expire | Scheduled per-link timer | Delete mapping from Map | Future lookup returns 404 |
| Health | GET health route | Check server process is active | Liveness response |
Why a Map was the correct first implementation
Map is a natural fit for key-to-value lookup in a single event-loop process. That makes it straightforward to reason about functionality and isolate API behavior from infrastructure.
But it creates clear limits: the process is the database. A restart drops all links. Two Node.js replicas would have different contents. A successful creation response does not mean the alias is durable. Describing that boundary precisely is more persuasive than treating a tiny demo as a distributed platform.
Expiry is a correctness contract, not merely a timer
expires_at and enforce expiry during every lookup. A background cleanup process could then reclaim old rows without becoming part of the correctness condition.
| Requirement | Current mechanism | Production-oriented design |
|---|---|---|
| Alias persistence | Process-local Map | PostgreSQL or Redis with an explicit durability policy |
| Expiry | setTimeout per link | Stored absolute expiration + lookup-time check |
| Multiple replicas | Separate incompatible Maps | Shared store accessed by all replicas |
| ID collision | Nano ID generated alias | Unique index + retry on duplicate key |
| Redirect safety | URL supplied by caller | Validate HTTP(S) scheme and destination policy |
| Abuse protection | Simple HTTP interface | Rate limits, logging, bot/abuse response |
| Idempotent creation | New alias for each request | Optional client idempotency key if needed |
Is an eight-character alias enough?
Failure and security review
| Failure | User impact | Design response |
|---|---|---|
| Server process restarts | Existing aliases disappear | Move mappings to shared persistence |
| Two replicas handle different requests | Alias created on A is missing on B | Centralize state |
| Timer fires late under heavy event-loop work | Expired alias may remain temporarily resolvable | Check expiry on reads |
| Unsafe redirect destination | Users can be sent to unwanted schemes/sites | Normalize and validate URL destinations |
| High create rate | Memory and timer count grow without bound | Rate limiting, storage TTL, quotas |
| A generated ID already exists | Existing destination can be overwritten | Atomic uniqueness check and retry |
What the implementation proves
Source



SaaimOpen to full-time roles, contract work, and conversations about things worth building.