Here are my insights I came up with while making my URL shortener
Introduction
URL shorteners look deceptively simple: store a long URL, generate a short code, and redirect users when the short link is visited.
In reality, a production-ready URL shortener must solve several engineering problems:
- Fast redirect performance
- High read throughput
- Abuse prevention
- Analytics collection
- Authentication and authorization
- Cache consistency
To explore these challenges, I built a URL shortener using Go, Gin, PostgreSQL, and Redis, with JWT-based authentication and analytics tracking.
Tech Stack
Backend
- Go
- Gin
- PostgreSQL
- Redis
- JWT Authentication
- Docker
Infrastructure
- GitHub Actions (CI)
- Docker Compose
- Redis Cache
- PostgreSQL Database
System Architecture
The application is organized into three primary layers:
Client
│
▼
Gin API Server
│
├── Redis Cache
│
└── PostgreSQL
API Layer
The API layer handles:
- URL creation
- URL redirects
- Authentication
- Analytics retrieval
- Request validation
- Rate limiting
Cache Layer
Redis serves two purposes:
- URL lookup caching
- Rate limiting
The cache significantly reduces database load for frequently accessed links by serving redirect requests directly from memory. Cache-aside architectures are commonly used in URL shorteners because they keep the database as the source of truth while reducing latency on hot paths.
Database Layer
PostgreSQL acts as the source of truth and stores:
- Users
- URLs
- Click events
- Analytics data
Data Model
The current schema revolves around three main entities:
Users
users
├── id
├── email
├── password_hash
└── created_at
URLs
urls
├── id
├── user_id
├── short_url
├── long_url
├── expiry
├── clicks
└── created_at
Click Events
click_events
├── id
├── url_id
├── ip_address
├── browser
├── device
├── country
├── referer
└── created_at
This relationship allows every URL to be associated with a specific user while maintaining detailed click analytics.
Authentication & Authorization
The application uses JWT-based authentication.
Registration Flow
User Registers
│
▼
Password Hashed (bcrypt)
│
▼
User Stored in PostgreSQL
│
▼
JWT Generated
Login Flow
Email + Password
│
▼
User Lookup
│
▼
Password Verification
│
▼
JWT Issued
The JWT contains the authenticated user's ID, which is injected into the request context by middleware.
JWT
│
▼
Auth Middleware
│
▼
userID in Context
This user ID is then used to authorize access to resources such as analytics dashboards.
URL Creation
Authenticated users can create short URLs with:
- Auto-generated short codes
- Custom aliases
- Expiration support
- Validation checks
Before a URL is stored:
- The request is validated.
- Custom aliases are checked for uniqueness.
- Ownership is attached using the authenticated user ID.
- The record is persisted in PostgreSQL.
Redirect Flow
The redirect endpoint is the most performance-sensitive part of the system.
Current redirect workflow:
Request
│
▼
Redis Lookup
│
├── Cache Hit
│ │
│ ▼
│ Redirect
│
└── Cache Miss
│
▼
PostgreSQL
│
▼
Update Cache
│
▼
Redirect
This follows the Cache-Aside pattern, where the application first checks Redis and falls back to PostgreSQL only when necessary. This approach is widely used to reduce database load and improve response latency.
Click Tracking
Every redirect generates analytics data.
The application records:
- Visitor IP address
- User agent
- Browser
- Device type
- Referrer
- Timestamp
Each redirect performs:
Redirect Request
│
▼
Increment Click Counter
│
▼
Create Click Event
│
▼
Redirect User
This enables analytics without requiring external tracking services.
Analytics Dashboard Backend
The analytics system currently supports:
Overview Metrics
- Total clicks
- Clicks today
- Clicks this week
- Clicks this month
Daily Activity
Daily click aggregation for chart visualizations.
Recent Visits
Recent visitor activity including:
- Browser
- Device
- Referrer
- Timestamp
Access Control
Analytics are protected through ownership checks.
Request Analytics
│
▼
Extract userID from JWT
│
▼
Fetch URL
│
▼
Verify Ownership
│
▼
Return Analytics
Users can only access analytics for URLs they own.
Rate Limiting
To prevent abuse, Redis-backed rate limiting is applied to API endpoints.
Different endpoints have different limits depending on their usage pattern.
Examples include:
- URL creation
- Redirect endpoints
- Analytics endpoints
This helps protect infrastructure while maintaining responsiveness.
What I Learned
Building this project highlighted several real-world backend engineering concepts:
- Cache-aside architecture
- Authentication vs authorization
- Redis caching strategies
- Analytics event collection
- Database relationship design
- API rate limiting
- Production-oriented Go application structure
More importantly, it demonstrated how quickly a seemingly simple service evolves into a system that requires thoughtful architecture and performance considerations.
What's Next
Planned improvements include:
- Analytics dashboard frontend in Next.js
- Browser and device distribution analytics
- Azure deployment
- Refresh token authentication
- Advanced dashboard metrics
- QR code generation
- Custom domains
- Observability and monitoring
The goal is to continue evolving the project into a production-grade URL shortening platform while exploring backend system design concepts in practice.
