Skip to content

Repository files navigation

dev.ly — URL Shortener & Analytics Platform

A high-performance URL shortener with a dedicated analytics microservice, real-time click tracking, Redis caching, BullMQ queues, and a modern Next.js dashboard.

FrontendBackendAnalytics


System Architecture

graph TB
subgraph Client Layer
FE[Next.js Frontend :3000]
end
subgraph API Layer
BE[Backend Service :5001]
AN[Analytics Service :5002]
WK[BullMQ Worker]
end
subgraph Cache Layer
RD[(Redis)]
end
subgraph Data Layer
PG[(PostgreSQL)]
end
FE -->|REST API| BE
FE -->|Stats API| AN
BE -->|1. URL Lookup| RD
RD -->|Cache Miss| PG
BE -->|2. Push Click Event| RD
RD -->|BullMQ Queue| WK
WK -->|3. Enrich & Write| PG
AN -->|Read Stats| PG
BE -.->|Rate Limit Check| RD
style FE fill:#7c3aed,color:#fff,stroke:none
style BE fill:#2563eb,color:#fff,stroke:none
style AN fill:#059669,color:#fff,stroke:none
style WK fill:#d97706,color:#fff,stroke:none
style RD fill:#dc2626,color:#fff,stroke:none
style PG fill:#0ea5e9,color:#fff,stroke:none
Loading

Redirect Flow

sequenceDiagram
participant C as Client
participant B as Backend
participant R as Redis
participant DB as PostgreSQL
participant Q as BullMQ Queue
participant W as Worker
C->>B: GET /:shortCode?utm_source=twitter
B->>R: GET url:shortCode
alt Cache Hit
R-->>B: { id, original_url }
else Cache Miss
B->>DB: SELECT FROM urls
DB-->>B: { id, original_url }
B->>R: SET url:shortCode (TTL 1h)
end
B-->>C: 302 Redirect (instant)
B->>R: INCR visit_count
B->>Q: Push { url_id, ip, ua, utm_* }
Q-->>W: Consume job
W->>R: Check dedup (SET key, 30s TTL)
W->>DB: INSERT url_analytics (enriched)
Loading

Repo Structure

This is a multi-repo project using Git submodules:

SubmoduleRepoDescription
frontend/dev.ly-frontendNext.js 16 dashboard & landing page
backend/dev.ly-backendExpress 5 URL shortener API
analytics/dev.ly-analyticsAnalytics microservice & BullMQ worker
dev.ly/
├── frontend/ ← git submodule (Next.js)
├── backend/ ← git submodule (Express API)
├── analytics/ ← git submodule (Analytics + Worker)
├── docker-compose.yml
├── .gitmodules
└── README.md ← you are here

Cloning

# Clone with all submodules
git clone --recurse-submodules https://github.com/dk-a-dev/dev.ly.git
# If already cloned without submodules
git submodule update --init --recursive

Tech Stack

LayerTechnology
FrontendNext.js 16, React 19, Tailwind CSS 4, Recharts, Framer Motion
BackendExpress 5, JWT, bcrypt, Redis, BullMQ
AnalyticsExpress 5, ua-parser-js, geoip-lite, BullMQ Workers
DatabasePostgreSQL 15
Cache / QueueRedis 7 + BullMQ
InfrastructureDocker, Docker Compose

Database Schema

erDiagram
users {
SERIAL id PK
VARCHAR username UK
VARCHAR email UK
VARCHAR password_hash
TIMESTAMP created_at
}
urls {
SERIAL id PK
TEXT original_url
VARCHAR short_code UK
INTEGER user_id FK
INTEGER visit_count
TIMESTAMP created_at
TIMESTAMP expires_at
}
url_analytics {
SERIAL id PK
INTEGER url_id FK
VARCHAR ip_address
TEXT user_agent
TEXT referrer
VARCHAR country
VARCHAR city
VARCHAR browser
VARCHAR os
VARCHAR device_type
VARCHAR utm_source
VARCHAR utm_medium
VARCHAR utm_campaign
BOOLEAN is_unique
TIMESTAMP visited_at
}
users ||--o{ urls : "creates"
urls ||--o{ url_analytics : "has clicks"
Loading

API Reference

Backend (:5001)

MethodEndpointAuthDescription
POST/api/auth/registerRegister user
POST/api/auth/loginLogin, returns JWT
GET/api/auth/profileGet current user profile
POST/api/urlCreate short URL
GET/api/urlList user's URLs
DELETE/api/url/:idDelete URL
GET/:shortCodeRedirect to original URL

Analytics Service (:5002)

MethodEndpointAuthDescription
POST/api/logIngest click event
GET/api/statsGet user's aggregate stats
GET/api/stats/urls-series7-day time series per URL
GET/api/stats/:idDetailed stats for single URL

Rate Limiting

EndpointLimitBehavior
POST /api/auth/*10 req/minReturns 429
POST /api/url30 req/minReturns 429
GET /api/stats/*30 req/minReturns 429
GET /:shortCodeNo limitAlways redirects

Quick Start

Prerequisites

  • Docker & Docker Compose
  • Node.js 18+ (for local dev)

Run with Docker (recommended)

# Clone with submodules
git clone --recurse-submodules https://github.com/dk-a-dev/dev.ly.git
cd dev.ly
# Start all services
docker-compose up --build -d
# View logs
docker-compose logs -f

This starts: PostgreSQL (:5432) → Redis (:6379) → Backend (:5001) → Analytics (:5002) → WorkerFrontend (:3000)

Run Locally (dev)

# Start DB + Redis only
docker-compose up -d db redis
# Backend (terminal 1)cd backend && npm install && node index.js
# Analytics API (terminal 2)cd analytics && npm install && node index.js
# Analytics Worker (terminal 3)cd analytics && node src/workers/worker.js
# Frontend (terminal 4)cd frontend && npm install && npm run dev

Environment Variables

Backend (.env)

PORT=5001DB_USER=postgresDB_PASSWORD=postgresDB_HOST=localhostDB_NAME=devlyDB_PORT=5432JWT_SECRET=supersecretkey_change_me_in_prodANALYTICS_URL=http://localhost:5002REDIS_URL=redis://localhost:6379

Analytics (.env)

PORT=5002DB_USER=postgresDB_PASSWORD=postgresDB_HOST=localhostDB_NAME=devlyDB_PORT=5432JWT_SECRET=supersecretkey_change_me_in_prodREDIS_URL=redis://localhost:6379

Docker: When running in Docker, use DB_HOST=db, ANALYTICS_URL=http://analytics:5002, REDIS_URL=redis://redis:6379


v2 Roadmap

FeatureDescriptionStatus
Redis URL CacheCache shortCode → URL in Redis. Sub-ms redirects.✅ Done
BullMQ Click QueueGuaranteed delivery. Zero lost analytics.✅ Done
Smart Rate LimitingRedis-backed. Protect APIs, never block redirects.✅ Done
Click DeduplicationSame IP + URL within 30s = is_unique: false.✅ Done
UTM TrackingParse utm_source, utm_medium, utm_campaign.✅ Done
QR Code GenerationAuto-generate QR codes for each short URL.🔲 Planned
Link Expiry DashboardManage and extend URL expiration from the UI.🔲 Planned

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages