A high-performance URL shortener built with Go, featuring comprehensive analytics, geolocation tracking, and a modern web interface.
- URL Shortening: Create short URLs from long ones
- Analytics Dashboard: View detailed click statistics and trends
- Geolocation Tracking: Track clicks by country and city using MaxMind GeoLite2
- Device & Browser Insights: Analyze user agents, devices, and operating systems
- Real-time Stats: HTMX-powered dynamic statistics updates
- Docker Support: Easy deployment with Docker Compose
- AWS Lambda Ready: Serverless deployment option included
- Backend: Go 1.22.5
- Web Framework: Chi v5
- Database: PostgreSQL
- Cache: Redis
- Geolocation: MaxMind GeoLite2
- Frontend: HTML5, CSS3, HTMX
- Deployment: Docker, Docker Compose
- Go: 1.22.5 or later
- PostgreSQL: 13+ (for production)
- Redis: 6+ (for caching)
- MaxMind GeoLite2 City Database: Download from MaxMind and place in
internal/analytics/GeoLite2-City.mmdb
-
Clone the repository
git clone <repository-url> cd url-shortner
-
Start the services
docker-compose up -d
-
Access the application
- Web UI: http://localhost:8080
- API endpoints available at the same URL
-
Prerequisites
- Go 1.22.5+
- PostgreSQL
- Redis
- MaxMind GeoLite2 City database (place in
internal/analytics/)
-
Install dependencies
go mod download
-
Set up environment variables Create a
.envfile:DATABASE_URL=postgres://user:password@localhost:5432/tinyurl?sslmode=disable REDIS_URL=redis://localhost:6379 ENV=development -
Run database migrations
# Apply migrations manually or use your preferred method psql -d tinyurl -f migrations/001_init.sql -
Start the server
go run ./cmd/server/main.go
go build -o bin/server ./cmd/server
./bin/serverCGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o bin/server ./cmd/servergo build -o bin/lambda ./cmd/lambdaRun the test suite:
go test ./...Run tests with coverage:
go test -cover ./...Run tests with race detection:
go test -race ./...- Format code:
go fmt ./... - Vet code:
go vet ./... - Run linter:
golangci-lint run(if installed)
The project uses PostgreSQL with manual migrations. To set up the database:
- Create a database named
tinyurl - Run the migration:
psql -d tinyurl -f migrations/001_init.sql
Copy .env.example to .env and fill in your values:
DATABASE_URL=postgres://user:password@localhost:5432/tinyurl?sslmode=disable
REDIS_URL=redis://localhost:6379
ENV=development
GET /- Main dashboardPOST /shorten- Create short URLGET /{code}- Redirect to original URLGET /{code}+- View URL statisticsGET /all_stats- View all URLs statisticsGET /stats/{code}- Get statistics partial (HTMX)
├── cmd/
│ ├── lambda/ # AWS Lambda deployment
│ └── server/ # HTTP server
├── internal/
│ ├── analytics/ # Click tracking and geo analytics
│ ├── db/ # Database connection
│ ├── handlers/ # HTTP handlers
│ ├── models/ # Data models
│ └── service/ # Business logic
├── migrations/ # Database migrations
├── web/
│ ├── static/ # CSS, JS, assets
│ └── templates/ # HTML templates
├── Dockerfile
├── docker-compose.yml
└── go.mod
The application uses environment variables for configuration:
DATABASE_URL: PostgreSQL connection stringREDIS_URL: Redis connection stringENV: Environment (development/production)
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Follow Go conventions:
- Use
go fmtto format your code - Run
go vetto check for common errors - Write tests for new functionality
- Follow the existing code style
- Use
- Commit your changes: Use clear, descriptive commit messages
- Push to your branch:
git push origin feature/your-feature-name - Create a Pull Request: Provide a clear description of your changes
- Follow standard Go formatting (
go fmt) - Use meaningful variable and function names
- Add comments for exported functions and types
- Keep functions small and focused
- Write comprehensive tests
docker-compose up -d- Build the Lambda binary
- Deploy using your preferred AWS tool (SAM, Serverless Framework, etc.)
- Configure environment variables in Lambda
- Build the binary for your target platform
- Set up PostgreSQL and Redis
- Configure environment variables
- Run the binary
Database Connection Errors
- Ensure PostgreSQL is running
- Check
DATABASE_URLformat - Verify database exists and user has permissions
Redis Connection Errors
- Ensure Redis is running on the specified port
- Check
REDIS_URLformat
GeoIP Database Missing
- Download GeoLite2-City.mmdb from MaxMind
- Place it in
internal/analytics/GeoLite2-City.mmdb
Port Already in Use
- Change the port in the code or kill the process using it
netstat -tulpn | grep :8080
Application logs are written to stdout/stderr. For Docker deployments, view logs with:
docker-compose logs -f appThis project is licensed under the MIT License - see the LICENSE file for details.


