A lightweight, high-performance RESTful URL Shortener service built with Spring Boot and PostgreSQL. This service allows users to convert long, cumbersome URLs into compact, 6-character unique codes and redirects users to the original URL when accessed.
- URL Shortening: Generates a secure, random 6-character alphanumeric code for any valid HTTP/HTTPS URL.
- Redirection: Instant HTTP 302 redirection from the short code to the original URL.
- Duplicate Prevention: Automatically regenerates short codes in case of collisions.
- Lombok Integration: Boilerplate-free, clean Java records and builder patterns.
- Robust Validation: Input validation to ensure only properly formatted URLs are shortened.
- PostgreSQL Backed: Uses a production-ready PostgreSQL database for data persistence.
- Framework: Spring Boot (v4.1.1)
- Language: Java 26
- Database: PostgreSQL (v18.3)
- ORM: Spring Data JPA / Hibernate (v7.4.5.Final)
- Connection Pool: HikariCP
- Validation: Jakarta Validation API
- Build Tool: Gradle 9.5.1
- Developer Utilities: Lombok (compile-only annotation processor)
sequenceDiagram
autonumber
actor User
participant Controller as UrlController
participant Service as UrlShortenerService
participant Repo as UrlMappingRepository
participant DB as Database (PostgreSQL)
User->>Controller: POST /api/urls { "url": "https://..." }
Controller->>Service: shortenUrl(url)
loop Code Generation
Service->>Service: generate 6-char random code
Service->>Repo: existsByShortCode(code)
Repo-->>Service: boolean (exists/not exists)
end
Service->>Repo: save(UrlMapping)
Repo->>DB: INSERT INTO url_mappings...
DB-->>Repo: Saved entity
Repo-->>Service: Saved entity
Service-->>Controller: Saved entity
Controller-->>User: 200 OK { shortUrl, shortCode, originalUrl }
sequenceDiagram
autonumber
actor User
participant Controller as UrlController
participant Service as UrlShortenerService
participant Repo as UrlMappingRepository
participant DB as Database (PostgreSQL)
User->>Controller: GET /api/urls/{shortCode}
Controller->>Service: getByShortCode(shortCode)
Service->>Repo: findByShortCode(shortCode)
Repo->>DB: SELECT * FROM url_mappings WHERE...
DB-->>Repo: Entity
Repo-->>Service: Entity
Service-->>Controller: Entity
Controller->>Controller: Increment click count (In memory)
Note over Controller: Note: See "Developer Insights" regarding persistence of click count.
Controller-->>User: 302 Found (Location: originalUrl)
The application maps URL associations using a single entity, UrlMapping, which translates to the url_mappings table:
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
id |
BIGINT |
PRIMARY KEY, GENERATED BY DEFAULT AS IDENTITY |
Unique database identifier |
original_url |
VARCHAR(2048) |
NOT NULL |
The original target URL (max length 2048 characters) |
short_code |
VARCHAR(10) |
NOT NULL, UNIQUE |
The unique 6-character lookup key |
click_count |
BIGINT |
NOT NULL, DEFAULT 0 |
Total redirection clicks tracked for this link |
- Endpoint:
POST /api/urls - Content-Type:
application/json - Request Body:
{ "url": "https://github.com/sukhmmeet/URL-Shortener-REST-API" } - Response (200 OK):
{ "originalUrl": "https://github.com/sukhmmeet/URL-Shortener-REST-API", "shortCode": "Ab8D2k", "shortUrl": "http://localhost:8080/Ab8D2k" }
- Endpoint:
GET /api/urls/{shortCode} - Response:
302 Found - Headers:
Location: The original URL to redirect to.
- JDK 26 installed on your system.
- PostgreSQL instance running locally.
- Gradle (or use the provided Gradle wrapper
./gradlew).
The application reads database credentials from environment variables. Set the following variables before running:
| Variable | Description | Example Value |
|---|---|---|
LOCAL_DB_USERNAME |
Database user | postgres |
LOCAL_DB_PASSWORD |
Database password | yourpassword |
Create a database named urlshortener in PostgreSQL:
CREATE DATABASE urlshortener;- Clone the repository and navigate to the project directory.
- Build the project:
./gradlew build
- Run the Spring Boot application:
./gradlew bootRun
- Access the API at
http://localhost:8080.
During a quick code review, we noticed a minor bug regarding the click statistics:
Warning
Click Count Persistence Issue
In the UrlController.java's redirection endpoint, the clickCount is incremented in memory on the UrlMapping object:
UrlMapping mapping = service.getByShortCode(shortCode);
mapping.setClickCount(mapping.getClickCount() + 1);However, because there is no @Transactional context or explicit call to repository.save(mapping), this increment is never saved back to the database.
How to Fix: Modify the controller or service method to persist the updated entity:
// In UrlController
mapping.setClickCount(mapping.getClickCount() + 1);
service.save(mapping); // or call repository.save(mapping) directly