A high-performance URL shortening service that converts long URLs into short, shareable links. Built with Spring Boot backend and responsive frontend, designed for scalability and reliability.
- Features
- Tech Stack
- Installation
- Usage
- API Documentation
- Architecture
- Deployment
- Configuration
- License
-
URL Shortening:
- Convert long URLs to short, memorable links
- Custom shortening algorithm (SHA-256 based)
- Idempotent operations - same URL = same short code
- Automatic URL validation
-
Link Management:
- Redirect long URLs from short links
- Track redirect statistics
- Delete expired/unwanted links
- Link expiration settings
-
Advanced Features:
- Hash collision handling with dynamic key length
- Duplicate URL detection
- Click tracking and analytics
- QR code generation for short URLs
- Batch URL shortening
- Input validation and sanitization
- SQL injection prevention
- CORS protection
- Rate limiting
- Admin-only delete operations
- Fast redirect (< 50ms)
- Optimized database queries
- Connection pooling
- Caching layer
- Horizontal scalability
- Docker containerization
- Docker Compose multi-container setup
- CI/CD ready
- Environment-based configuration
- Health check endpoints
| Layer | Technologies |
|---|---|
| Backend | Java 17+, Spring Boot 3.0+ |
| Framework | Spring Framework, Spring Data JPA |
| Database | MySQL 8.0+, Hibernate |
| Frontend | HTML5, CSS3, Vanilla JavaScript |
| Containerization | Docker, Docker Compose |
| Build Tool | Maven |
| API | RESTful, JSON |
- Java Development Kit (JDK): 17 or higher
- MySQL: 8.0 or higher
- Docker (optional): for containerized deployment
- Maven: 3.6+ (or use Maven wrapper)
-
Clone the repository:
git clone https://github.com/Ahmed122000/URL_Shortener.git cd URL_Shortener -
Configure MySQL Database:
CREATE DATABASE urls_db; CREATE USER 'url_user'@'localhost' IDENTIFIED BY 'password123'; GRANT ALL PRIVILEGES ON urls_db.* TO 'url_user'@'localhost'; FLUSH PRIVILEGES;
-
Create
.envfile (or editapplication.properties):spring.datasource.url=jdbc:mysql://localhost:3306/urls_db spring.datasource.username=url_user spring.datasource.password=password123 spring.jpa.hibernate.ddl-auto=update server.port=8080 url.shortener.domain=http://localhost:8080/api
-
Build the project:
mvn clean install
-
Run the application:
mvn spring-boot:run
-
Access the application:
Backend API: http://localhost:8080 Frontend: http://localhost:8080 (if served)
-
Navigate to frontend directory:
cd frontend -
Open in browser:
- Simply open
index.htmlin your browser - Or use a local server:
python -m http.server 8081
- Simply open
-
Navigate to project root:
cd URL_Shortener -
Update
docker-compose.ymlwith your environment variables:environment: DATABASE_URL: jdbc:mysql://mysql-db:3306/urls_db DATABASE_USERNAME: url_user DATABASE_PASSWORD: your_password CURRENT_DOMAIN: http://localhost:8080/api MYSQL_ROOT_PASSWORD: root_password MYSQL_DATABASE: urls_db MYSQL_USER: url_user MYSQL_PASSWORD: your_password
-
Start all services:
docker-compose up --build
-
Access the services:
- Backend API:
http://localhost:8080 - MySQL:
localhost:3306 - Frontend:
http://localhost:3000(if configured)
- Backend API:
-
Stop services:
docker-compose down
- Access the application:
http://localhost:8080 - Enter a long URL in the input field
- Click "Shorten" button
- Copy the short URL from the result
- Share the short link with others
curl -X POST http://localhost:8080/api/url \
-H "Content-Type: text/plain" \
-d "https://www.example.com/very/long/url/that/needs/shortening"Response:
{
"urlKey": "abc123",
"fullUrl": "https://www.example.com/very/long/url/that/needs/shortening",
"shortUrl": "http://localhost:8080/api/abc123",
"createdAt": "2026-04-25T10:30:00"
}curl -X GET http://localhost:8080/api/abc123 -LResult: Redirects to the original URL
curl -X GET http://localhost:8080/api/abc123/detailsResponse:
{
"urlKey": "abc123",
"fullUrl": "https://www.example.com/...",
"shortUrl": "http://localhost:8080/api/abc123",
"createdAt": "2026-04-25T10:30:00",
"clicks": 42,
"lastAccessed": "2026-04-25T15:45:00"
}curl -X DELETE http://localhost:8080/api/abc123 \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"Response:
{
"message": "URL deleted successfully"
}http://localhost:8080/api
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/url |
Shorten a URL | No |
GET |
/{key} |
Redirect to original URL | No |
GET |
/{key}/details |
Get URL details & stats | No |
GET |
/stats |
Get all statistics | Admin |
DELETE |
/{key} |
Delete a URL | Admin |
POST |
/batch |
Shorten multiple URLs | No |
GET |
/health |
Health check | No |
Request:
Content-Type: text/plain
https://medium.com/@user/very-long-article-title-about-web-development-2024
Success Response (200):
{
"urlKey": "xK9mP2",
"fullUrl": "https://medium.com/@user/very-long-article-title-about-web-development-2024",
"shortUrl": "http://localhost:8080/api/xK9mP2",
"createdAt": "2026-04-25T10:30:00Z"
}Error Response (400):
{
"error": "Invalid URL format",
"message": "Please provide a valid URL"
}Request:
GET /api/xK9mP2
Response (302):
Location: https://medium.com/@user/very-long-article-title-about-web-development-2024
βββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (HTML/CSS/JS) β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β Spring Boot Application (REST API) β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β Controllers β β
β β - URLController β β
β β - StatsController β β
β ββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β Services β β
β β - URLService β β
β β - HashingService (SHA-256) β β
β β - AnalyticsService β β
β ββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β Repository (JPA) β β
β β - URLRepository β β
β ββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β MySQL Database β
β ββββββββββββββββββββββββββββββββββββββ β
β β URLs Table β β
β β - id β β
β β - url_key (unique) β β
β β - full_url β β
β β - created_at β β
β β - click_count β β
β ββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββ
- URL Controller: Handles HTTP requests
- URL Service: Business logic for shortening
- Hashing Algorithm: SHA-256 with collision handling
- Repository: Database access via JPA
- Cache Layer: Improves redirect performance
// If collision occurs, increase key length
String key = generateKey(url, length);
while (repository.existsByUrlKey(key)) {
length++;
key = generateKey(url, length);
}- Same URL input always produces same short code
- Prevents duplicate entries
- Efficient storage
- Connection pooling
- Query optimization
- Caching redirects
- Batch processing support
version: '3.8'
services:
mysql-db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: urls_db
MYSQL_USER: url_user
MYSQL_PASSWORD: password123
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
app:
build: .
environment:
DATABASE_URL: jdbc:mysql://mysql-db:3306/urls_db
DATABASE_USERNAME: url_user
DATABASE_PASSWORD: password123
CURRENT_DOMAIN: http://localhost:8080/api
ports:
- "8080:8080"
depends_on:
- mysql-db
volumes:
mysql_data:# Server
server.port=8080
server.servlet.context-path=/
# Database
spring.datasource.url=jdbc:mysql://localhost:3306/urls_db
spring.datasource.username=url_user
spring.datasource.password=password123
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
# URL Shortener
url.shortener.domain=http://localhost:8080/api
url.shortener.initial-key-length=6
url.shortener.max-key-length=10
# Logging
logging.level.root=INFO
logging.level.com.app=DEBUGCREATE TABLE urls (
id INT PRIMARY KEY AUTO_INCREMENT,
url_key VARCHAR(20) UNIQUE NOT NULL,
full_url TEXT NOT NULL,
short_url VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_accessed TIMESTAMP NULL,
click_count INT DEFAULT 0,
expires_at TIMESTAMP NULL,
active BOOLEAN DEFAULT TRUE
);
CREATE INDEX idx_url_key ON urls(url_key);
CREATE INDEX idx_full_url ON urls(full_url(100));-
Build JAR:
mvn clean package -DskipTests
-
Using Docker:
docker build -t url-shortener . docker run -p 8080:8080 url-shortener -
Environment Variables:
export DATABASE_URL=jdbc:mysql://prod-db:3306/urls_prod export DATABASE_USERNAME=prod_user export DATABASE_PASSWORD=strong_password export CURRENT_DOMAIN=https://short.example.com
-
Using Kubernetes (optional):
kubectl apply -f k8s-deployment.yaml
Run tests:
mvn testRun with coverage:
mvn test jacoco:report# Check MySQL is running
mysql -u root -p -e "SELECT 1;"
# Verify credentials in application.properties# Change port in application.properties
server.port=8081# Rebuild containers
docker-compose down -v
docker-compose up --build- Analytics dashboard
- Custom short codes
- QR code generation
- API rate limiting per user
- Redis caching layer
- Multi-tenancy support
- Webhook notifications
- Mobile app
This project is licensed under the MIT License - see LICENSE file for details.
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
- π§ Email: ahmedhesham122000@gmail.com
- π GitHub: @Ahmed122000
- π Issues: GitHub Issues
Last Updated: April 2026 | Built with β€οΈ by Ahmed Hesham