URL Shortener | Java, Spring Boot, Redis, PostgreSQL, Docker
- Architected a scalable URL shortening service using Spring Boot 3.2, PostgreSQL, and Redis, integrating a GitHub Actions CI pipeline to automate build verification and enforce unit test execution prior to deployment.
- Designed a redirection layer using a cache-aside Redis strategy (24h TTL) to mitigate database read spikes and deliver sub-millisecond cache lookup latency via Redis for frequently accessed URLs.
- Developed a Base62 encoding mechanism supporting 56+ billion unique short URLs (6-character depth), validated through JUnit 5 and Mockito test suites covering controller, service, and utility layers to ensure logical correctness and edge-case handling.
- URL Shortening: Convert long URLs to short, shareable links
- Custom Aliases: Create memorable custom short codes
- Click Tracking: Real-time click count statistics
- URL Expiration: Optional expiration for temporary links
- Performance: Sub-millisecond cache lookups via Redis
- RESTful API: Clean, documented REST endpoints (Swagger UI available locally)
The application provides interactive API documentation via Swagger UI.
Overview of available endpoints
Testing an endpoint directly from the browser
┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐
│ API Client │────▶│ Spring Boot │────▶│ Redis │
│ (Browser/App) │◀────│ REST API │◀────│ Cache │
└─────────────────┘ └──────────────────┘ └────────────────┘
│ │
│ Cache Miss │
▼ ▼
┌──────────────────┐ ┌────────────────┐
│ PostgreSQL │────▶│ Populate │
│ Database │ │ Cache │
└──────────────────┘ └────────────────┘
| Component | Choice | Rationale |
|---|---|---|
| Encoding | Base62 | 6 chars = 56B URLs, URL-safe characters |
| Caching | Redis | Sub-ms reads, 24h TTL for hot URLs |
| Database | PostgreSQL | ACID compliance, reliable storage |
| Redirect | 302 Found | Allows accurate click tracking |
- Java 21 (LTS)
- Docker & Docker Compose
Note: Maven is included via the Maven Wrapper (
./mvnw) - no separate installation needed!
# Start PostgreSQL and Redis
docker-compose up -d
# Verify containers are running
docker-compose ps# Using Maven Wrapper (recommended)
./mvnw spring-boot:run
# Or build JAR and run
./mvnw clean package
java -jar target/url-shortener-1.0.0.jar# Create a short URL
curl -X POST http://localhost:8080/api/v1/shorten \
-H "Content-Type: application/json" \
-d '{"originalUrl": "https://www.google.com/search?q=very+long+url"}'
# Response:
# {
# "shortCode": "1",
# "shortUrl": "http://localhost:8080/1",
# "originalUrl": "https://www.google.com/search?q=very+long+url"
# }
# Test redirect (follow redirects)
curl -L http://localhost:8080/1
# Get URL statistics
curl http://localhost:8080/api/v1/urls/1/statsPOST /api/v1/shorten
Content-Type: application/json
{
"originalUrl": "https://example.com/very/long/url", // Required
"customAlias": "mylink", // Optional
"expiresInHours": 24 // Optional
}Response: 201 Created
{
"shortCode": "mylink",
"shortUrl": "http://localhost:8080/mylink",
"originalUrl": "https://example.com/very/long/url",
"createdAt": "2024-01-15T10:30:00",
"expiresAt": "2024-01-16T10:30:00"
}GET /{shortCode}Response: 302 Found with Location header
GET /api/v1/urls/{shortCode}/statsResponse: 200 OK
{
"shortCode": "mylink",
"shortUrl": "http://localhost:8080/mylink",
"originalUrl": "https://example.com/very/long/url",
"clickCount": 42,
"createdAt": "2024-01-15T10:30:00",
"expiresAt": "2024-01-16T10:30:00",
"isExpired": false
}# Run all tests
./mvnw test
# Run with coverage
./mvnw test jacoco:reportKey configuration in application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5433/urlshortener
username: postgres
password: postgres
data:
redis:
host: localhost
port: 6379
app:
base-url: http://localhost:8080"Designed a URL shortener using Base62 encoding and Redis caching to support high read concurrency using Redis caching for sub-millisecond lookups."
-
Base62 Encoding
- Maps database ID to short string
- 6 characters = 56 billion unique URLs
- Characters:
[0-9a-zA-Z]
-
Redis Caching
- Cache-aside pattern
- 24-hour TTL for hot URLs
- Sub-millisecond lookups
-
Atomic Counters
- Uses database-level atomic increment operations
- Thread-safe click tracking
- Prevents lost updates under concurrent requests
-
Clean Architecture
- Controller → Service → Repository
- Separation of concerns
- Testable design
src/main/java/com/urlshortener/
├── UrlShortenerApplication.java # Main application
├── config/
│ └── RedisConfig.java # Redis configuration
├── controller/
│ └── UrlController.java # REST endpoints
├── dto/
│ ├── ShortenRequest.java # Request DTO
│ ├── ShortenResponse.java # Response DTO
│ ├── UrlStatsResponse.java # Stats DTO
├── entity/
│ └── Url.java # JPA entity
├── exception/
│ ├── GlobalExceptionHandler.java
│ ├── UrlNotFoundException.java
│ └── UrlExpiredException.java
├── repository/
│ └── UrlRepository.java # JPA repository
├── service/
│ ├── UrlService.java # Service interface
│ └── UrlServiceImpl.java # Service implementation
└── util/
│ └── Base62Encoder.java # Encoding utility
MIT License - feel free to use this for learning and interviews!