A modern, full-stack URL shortening service built with Spring Boot and deployed on Render. Transform long URLs into short, shareable links with click tracking and custom code support.
β οΈ Note: The deployed service may experience latency (30-60 seconds on first request) due to cold starts and slower VMs on Render's free tier. The application spins down after 15 minutes of inactivity. Subsequent requests after the initial wake-up are fast.
- π Lightning Fast - Instant URL shortening with optimized backend
- π Click Tracking - Monitor how many times your links are clicked
- π¨ Custom Short Codes - Create memorable links with personalized codes
- π Secure - Environment-based configuration for sensitive data
- π Responsive UI - Beautiful, mobile-friendly interface built with Tailwind CSS
- β‘ Auto-generated Codes - 6-character alphanumeric codes for quick sharing
- Spring Boot 4.0.1 - Modern Java framework
- Spring Data JPA - Database interaction
- Hibernate - ORM with PostgreSQL dialect
- Maven - Dependency management
- Lombok - Reduce boilerplate code
- HTML5 / CSS3 - Semantic markup and styling
- Tailwind CSS - Utility-first CSS framework
- JavaScript (Vanilla) - Dynamic interactions
- Font Awesome - Icon library
- PostgreSQL (Supabase) - Cloud-hosted database
- Render - Backend deployment platform
- HikariCP - Connection pooling
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client Layer β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Browser β β Mobile β β Desktop β β
β β (HTML/CSS) β β App β β App β β
β ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ β
β β β β β
β βββββββββββββββββββββββββββΌβββββββββββββββββββββββββ β
β β β
β HTTP/HTTPS (REST API) β
βββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer (Render) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Spring Boot Application β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β β β Static β β REST β β Redirect β β β
β β β Content β β Controller β β Controller β β β
β β β (index.html) β β (/api/*) β β (/s/*) β β β
β β ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ β β
β β β β β β β
β β ββββββββββββββββββββΌβββββββββββββββββββ β β
β β β β β
β β ββββββββΌββββββ β β
β β β β β β
β β β Service β β β
β β β Layer β β β
β β β β β β
β β ββββββββ¬ββββββ β β
β β β β β
β β ββββββββΌβββββββ β β
β β β JPA/ β β β
β β β Hibernate β β β
β β β Repository β β β
β β ββββββββ¬βββββββ β β
β β β β β
β ββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββ β
β β β
β ββββββββΌββββββββ β
β β HikariCP β β
β β (Connection β β
β β Pool) β β
β ββββββββ¬ββββββββ β
β β β
β JDBC over SSL/TLS β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ β
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Persistence Layer (Supabase) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PostgreSQL Database (EU Region) β β
β β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Table: url β β β
β β β ββββββββββββββββββββββββββββββββββββββββββββββββ β β β
β β β β id (BIGSERIAL PK) β β β β
β β β β original_url (VARCHAR 2048) β β β β
β β β β short_code (VARCHAR 10 UNIQUE) β β β β
β β β β click_count (BIGINT) β β β β
β β β β created_at (TIMESTAMP) β β β β
β β β ββββββββββββββββββββββββββββββββββββββββββββββββ β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β
β β Features: Connection Pooler (Port 6543) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Components:
βββ Frontend: Vanilla JS + Tailwind CSS (SPA served as static content)
βββ Backend: Spring Boot 4.0.1 with embedded Tomcat
βββ API: RESTful endpoints (POST /api/shorten, GET /api/info/:code)
βββ Redirect: GET /s/:code β 302 redirect to original URL
βββ ORM: Hibernate with Spring Data JPA repositories
βββ Connection Pool: HikariCP (max 5 connections, min idle 2)
βββ Database: PostgreSQL 13+ hosted on Supabase (EU)
### Request Flow
1. **URL Shortening Flow:**
- User submits URL via frontend form
- AJAX POST request to `/api/shorten` with JSON payload
- UrlController validates and forwards to UrlService
- UrlService generates/validates short code
- JPA Repository persists to PostgreSQL via HikariCP
- Response returns shortened URL to frontend
2. **Redirect Flow:**
- User clicks short URL (e.g., `https://app.onrender.com/s/abc123`)
- GET request to `/s/abc123`
- UrlController fetches original URL from UrlService
- Click count incremented atomically
- HTTP 302 redirect to original URL
3. **Info Retrieval Flow:**
- API request to `/api/info/abc123`
- Returns JSON with URL stats (clicks, creation date, original URL)
## π Getting Started
### Prerequisites
- Java 21 or higher
- Maven 3.6+
- PostgreSQL database (or Supabase account)
### Local Development
1. **Clone the repository**
```bash
git clone https://github.com/yourusername/url_shortener.git
cd url_shortener
- Create
.envfile
PORT=8080
APP_BASE_URL=http://localhost:8080
DB_URL=jdbc:postgresql://your-db-host:5432/postgres
DB_USERNAME=your_username
DB_PASSWORD=your_password- Build the project
mvn clean install- Run the application
mvn spring-boot:run- Access the application
http://localhost:8080
- Create a Dockerfile (if not exists)
FROM openjdk:21-jdk-slim
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]- Push code to GitHub
git add .
git commit -m "Initial commit"
git push origin main-
Create a new Web Service on Render
- Connect your GitHub repository
- Select Docker as the environment
- Select region (preferably EU to match Supabase)
- Render will automatically detect the Dockerfile
- No need to specify build or start commands (handled by Docker)
-
Set Environment Variables
PORT- Automatically provided by RenderAPP_BASE_URL- Your Render URL (e.g.,https://your-app.onrender.com)DB_URL- Your Supabase connection string (use pooler port 6543)DB_USERNAME- Database usernameDB_PASSWORD- Database password
-
Deploy!
- Render will build the Docker image and deploy your application
- First deployment may take 2-5 minutes
- Free tier instances spin down after 15 minutes of inactivity
- Create a new project on Supabase
- Navigate to Settings β Database
- Copy the connection string (use Pooler for better performance with port 6543)
- Update your environment variables
POST /api/shorten
Content-Type: application/json
{
"url": "https://example.com/very/long/url",
"customCode": "mycode" // Optional
}Response:
{
"originalUrl": "https://example.com/very/long/url",
"shortUrl": "https://your-app.onrender.com/s/mycode",
"shortCode": "mycode",
"createdAt": "2026-01-07T19:00:00Z",
"clickCount": 0
}GET /api/info/{shortCode}GET /s/{shortCode}# Database Configuration
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
# JPA Configuration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
# Server Configuration
server.port=${PORT:8080}CREATE TABLE url (
id BIGSERIAL PRIMARY KEY,
original_url VARCHAR(2048) NOT NULL,
short_code VARCHAR(10) UNIQUE NOT NULL,
click_count BIGINT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);