A URL shortening service built in Go with comprehensive testing. This is a development/learning project.
- URL shortening with base62 encoding
- Dual storage backends (in-memory or Redis) with atomic operations
- HTTP server with Gin framework
- Middleware (CORS, logging, recovery, validation)
- Environment-based configuration
- Graceful shutdown with timeout handling
- Test coverage (unit, integration, benchmark, race detection)
- URL expiration support
- Access statistics tracking
POST /urls
Content-Type: application/json
{
"long_url": "https://www.example.com/very/long/path",
"expiration_date": "2025-12-31T23:59:59Z" // optional
}Response:
{
"short_url": "http://localhost:8080/1"
}GET /{shortCode}Returns 302 Found with Location header pointing to the original URL.
GET /urls/{shortCode}/statsResponse:
{
"short_code": "1",
"long_url": "https://www.example.com/very/long/path",
"access_count": 42,
"created_at": "2025-07-19T17:00:00Z",
"expiration_date": "2025-12-31T23:59:59Z"
}GET /health- Go 1.21 or later
- Docker (for Redis storage)
# Clone the repository
git clone <repository-url>
cd tiny-url
# Install dependencies
go mod tidy
# Build the service
go build -o tiny-url-service# In-memory storage (quick start)
go run .
# With Redis persistence
docker-compose up -d
STORAGE_TYPE=redis go run .The service is deployed on Railway with managed Redis:
- Demo URL: https://tiny-url-production.up.railway.app
- Health Check: https://tiny-url-production.up.railway.app/health
- API Testing: Use the live API for demonstrations
# Start Redis locally
docker-compose up -d
# Stop Redis
docker-compose down
# Access Redis CLI
docker exec -it tiny-url-redis redis-cli| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Server port |
GIN_MODE |
debug |
Gin mode (debug, release, test) |
BASE_URL |
http://localhost:8080 |
Base URL for short links |
STORAGE_TYPE |
memory |
Storage backend (memory or redis) |
REDIS_URL |
redis://localhost:6379/0 |
Redis connection URL |
READ_TIMEOUT |
10s |
HTTP read timeout |
WRITE_TIMEOUT |
10s |
HTTP write timeout |
IDLE_TIMEOUT |
60s |
HTTP idle timeout |
The project includes a docker-compose.yml file for easy Redis setup:
services:
redis:
image: redis:7-alpine
container_name: tiny-url-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes --appendfsync everysec
restart: unless-stopped| Feature | In-Memory | Redis |
|---|---|---|
| Data Persistence | β Lost on restart | β Persists across restarts |
| Multiple Instances | β Single instance only | β Supports multiple instances |
| Performance | β‘ Fastest | π Fast (network overhead) |
| Memory Usage | πΎ Process memory | πΎ Redis memory |
| Setup Complexity | β Zero setup | π³ Requires Docker/Redis |
| Production Ready | β Development only | β Production ready |
URLs are stored as JSON in Redis with the following structure:
# Keys
counter # Atomic counter for unique IDs
url:{shortCode} # URL mapping data
# Example data
GET url:1
> {"id":1,"short_code":"1","long_url":"https://example.com","created_at":"2025-07-19T17:30:00Z"}# Run complete test suite (39 tests)
go test ./... -v
# With coverage report
go test ./... -cover
# Race condition detection
go test ./... -race- Storage: 92.4% coverage (Redis + Memory)
- Middleware: 100% coverage (Rate limiting)
- Utils: 96.8% coverage (Encoding + Validation)
- Integration: Full API coverage
# Storage tests (with Redis mocking)
go test ./storage -v
# Rate limiter tests
go test ./middleware -v
# Integration tests
go test ./tests -v
# Utility tests
go test ./utils -vtiny-url/
βββ main.go # Application entry point
βββ config/
β βββ config.go # Environment configuration
βββ models/
β βββ url.go # Data models
βββ storage/
β βββ interface.go # Storage interface
β βββ memory.go # In-memory implementation
β βββ redis.go # Redis implementation
βββ handlers/
β βββ url_handlers.go # HTTP request handlers
β βββ server.go # Server setup and middleware
βββ utils/
β βββ encoding.go # Base62 encoding/decoding
β βββ validation.go # URL validation
βββ tests/
β βββ integration_test.go # Integration tests
β βββ benchmark_test.go # Performance benchmarks
βββ middleware/
β βββ rate_limiter_test.go # Rate limiting tests
βββ storage/
β βββ redis_test.go # Redis storage tests (with mocking)
β βββ memory_test.go # Memory storage tests
βββ docs/
βββ ARCHITECTURE.md # System architecture documentation
βββ API.md # API reference and examples
- Converts numeric IDs to short, URL-safe strings
- Character set:
0-9A-Za-z(62 characters) - Collision-free through atomic counter incrementation
In-Memory Storage:
- Mutex-protected concurrent access
- Atomic counter for unique ID generation
- Zero-allocation retrieval operations
- Fast development/testing, data lost on restart
Redis Storage:
- Persistent data across restarts
- Atomic counters using Redis INCR
- JSON serialization of URL mappings
- Support for multiple app instances
- Production-ready with data durability
- Environment-based configuration
- Structured logging with Gin
- Panic recovery middleware
- CORS support
- Content-type validation
- Graceful shutdown with signal handling
Based on benchmark tests:
In-Memory Storage:
| Operation | Throughput | Memory per Op |
|---|---|---|
| Store URL | ~2M ops/sec | 201 B |
| Retrieve URL | ~13M ops/sec | 0 B |
| Create Short URL (HTTP) | ~50K req/sec | - |
| Redirect (HTTP) | ~100K req/sec | - |
Redis Storage:
| Operation | Throughput | Memory per Op |
|---|---|---|
| Store URL | ~50K ops/sec | 512 B |
| Retrieve URL | ~100K ops/sec | 256 B |
| Create Short URL (HTTP) | ~10K req/sec | - |
| Redirect (HTTP) | ~20K req/sec | - |
Race Condition Testing: β
All concurrent access tests pass with -race flag for both storage backends
- Architecture Guide - System design, components, and data flow
- API Reference - Complete API documentation with examples
- Project Plan - Development phases and Railway deployment setup
- 39 comprehensive tests with Redis mocking
- 92.4% storage coverage, 100% middleware coverage
- Integration tests cover all API endpoints
- Race detection prevents concurrency bugs
- Benchmark tests ensure performance
- β Persistent Storage: Redis implementation complete
- β Distributed Counter: Redis INCR provides atomic counters across instances
- β Rate Limiting: Per-IP token bucket (20 req/min) implemented
- Security: Add authentication, input sanitization
- Monitoring: Add metrics, health checks, observability
- Error Handling: More robust error responses and logging
- Configuration: More comprehensive config validation
- Performance: Connection pooling, caching, optimization
- Deployment: Containerization, CI/CD, infrastructure