You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A high-performance URL shortening platform built with FastAPI, designed to scale from day one. Architecture mirrors real-world systems like Bitly and TinyURL with an emphasis on low-latency redirects, async analytics, and Redis-first caching.
Batch size and flush interval are configurable via ANALYTICS_BATCH_SIZE / ANALYTICS_FLUSH_INTERVAL.
Caching Strategy
Key Pattern
Content
TTL
url:{short_code}
Serialized URL payload
1 hour (2× for hot URLs)
hot:{short_code}
Click counter
1 hour (rolling)
qr:{short_code}
Base64 PNG
24 hours
analytics:summary:{code}
Aggregated stats
5 minutes
ratelimit:{id}:{endpoint}
Sorted set (timestamps)
Window duration
analytics:queue
List of click events
—
Hot URL detection: when a link crosses HOT_URL_CLICK_THRESHOLD clicks, its Redis TTL is doubled automatically, preventing popular links from ever expiring from cache.
Rate Limiting
Implemented as a sliding window using Redis sorted sets, which avoids the burst-at-boundary problem of fixed windows:
On each request:
1. ZREMRANGEBYSCORE key -inf (now - window) # remove stale entries
2. ZCARD key # current count
3. If count < limit: ZADD key {now} {uuid} # admit request
4. Else: reject 429 # deny request
Limits (configurable):
Authenticated users: 100 req / 60s
Anonymous: 20 req / 60s
Registration: 10 attempts / hour
Login: 20 attempts / 5 minutes
Tech Stack
Component
Technology
Framework
FastAPI 0.115
Runtime
Python 3.12 + uvicorn
Database
PostgreSQL 16 via asyncpg
ORM
SQLAlchemy 2.0 (async)
Migrations
Alembic
Cache
Redis 7 (redis[asyncio])
Auth
JWT (python-jose) + bcrypt (passlib)
Validation
Pydantic v2
QR Codes
qrcode + Pillow
UA Parsing
user-agents
Logging
structlog (JSON in prod)
Testing
pytest-asyncio + httpx
Container
Docker + Docker Compose
Project Structure
app/
├── api/v1/endpoints/ # Thin route handlers — no business logic
│ ├── auth.py
│ ├── urls.py
│ ├── analytics.py
│ ├── qr.py
│ └── health.py
├── analytics/ # Event value objects + background processor
├── cache/ # Redis client, URL cache, rate limiter, analytics queue
├── core/ # Security, exceptions, structured logging
├── db/ # SQLAlchemy base + async session factory
├── dependencies/ # FastAPI dependency injection (auth, db, cache)
├── middleware/ # Request logging with request-ID tracing
├── models/ # SQLAlchemy ORM models
├── repositories/ # Data access layer (no business logic)
├── schemas/ # Pydantic v2 request/response models
├── services/ # Business logic layer
├── tasks/ # Background worker lifecycle
├── tests/ # pytest-asyncio test suite
└── utils/ # Short code generation, URL validator, UA parser
Quick Start
With Docker (recommended)
# 1. Clone and configure
cp .env.example .env
# Edit .env — at minimum set SECRET_KEY to a strong random value# 2. Start all services (Postgres, Redis, API)
docker compose up --build
# 3. Run migrations (first time only — handled by the `migrate` service)
docker compose run --rm migrate
# 4. API is live at http://localhost:8000# 5. Interactive docs at http://localhost:8000/docs (DEBUG=true only)