System Design
Design With Sid
spring boot

Building a URL Shortener

Building a URL Shortener

URL Shortener with Spring Boot

In this blog, we walk through the process of building a URL Shortener application using Spring Boot and JPA. You will learn how to:

  • Hash long URLs into shorter versions.
  • Persist URL data in an in-memory H2 database.
  • Create a REST API for shortening and retrieving URLs.

Step 1: Setting Up the Project

To get started, we will create a Spring Boot project with the necessary dependencies using start.spring.io.

Project Setup:

  1. Go to start.spring.io.
  2. Choose the following configurations:
    • Project Type: Maven
    • Language: Java
    • Spring Boot Version: Use the latest stable version
    • Group: com.sid121212
    • Artifact: url-shortener
  3. Under Dependencies, add the following:
    • Spring Web: For creating REST APIs
    • Spring Data JPA: For database interaction
    • H2 Database: For an in-memory database
  4. Click Generate to download the project.

Import the Project:

  1. Unzip the downloaded project.
  2. Open it in your favorite IDE (IntelliJ, Eclipse, etc.).
  3. Once opened, the project structure will have all the necessary Spring Boot configurations to begin.

Now that we have the basic setup ready, let’s move on to building the core URL Shortener functionality.

Step 2: Creating the ShortUrl Entity

In this step, we will create the ShortUrl entity, which will represent the data model for storing long URLs and their corresponding shortened versions.

Entity Class: ShortUrl.java

In the src/main/java/com/sid121212/url_shortner/ directory, create a file named ShortUrl.java with the following content:

package com.sid121212.url_shortner;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class ShortUrl {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	private String hashedId;
	private String longUrl;
	
	public ShortUrl(Long id, String hashedId, String longUrl) {
		this.id = id;
		this.hashedId = hashedId;
		this.longUrl = longUrl;
	}
	
	public ShortUrl() {
        // Default constructor required by JPA
    }
    
    public ShortUrl(String longUrl) {
        this.longUrl = longUrl;
    }

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getHashedId() {
		return hashedId;
	}
	public void setHashedId(String hashedId) {
		this.hashedId = hashedId;
	}
	public String getLongUrl() {
		return longUrl;
	}
	public void setLongUrl(String longUrl) {
		this.longUrl = longUrl;
	}
}

Step 3: Creating the UrlService Interface

Now that we have the ShortUrl entity, the next step is to define the service layer that will handle the business logic for saving and retrieving shortened URLs. We'll start by creating an interface for the service.

Service Interface: UrlService.java

In the src/main/java/com/sid121212/url_shortner/controller/service/ directory, create a file named UrlService.java with the following content:

package com.sid121212.url_shortner.controller.service;

public interface UrlService {

    public String saveUrl(String longUrl);
    public String getShortUrl(String hashedId);
}

Step 4: Creating the Repository

To interact with the database, we'll need a repository to handle CRUD operations for the ShortUrl entity. We'll use Spring Data JPA for this purpose.

Repository: JpaRepo.java

In the src/main/java/com/sid121212/url_shortner/controller/repo/ directory, create a file named JpaRepo.java with the following content:

package com.sid121212.url_shortner.controller.repo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.sid121212.url_shortner.ShortUrl;

public interface JpaRepo extends JpaRepository<ShortUrl, Long> {

    @Query("SELECT s.longUrl FROM ShortUrl s WHERE s.hashedId = :hashedId")
    String getByHashedId(String hashedId);
}

Step 5: Implementing the URL Shortening Logic

The core functionality of the URL shortener lies in its service layer. This is where we handle the logic for converting long URLs into short ones and retrieving them back. Let’s walk through the service implementation.

Service: UrlServiceImpl.java

In the src/main/java/com/sid121212/url_shortner/controller/service/ directory, create a file named UrlServiceImpl.java with the following content:

Explanation:

saveUrl(String longUrl)

  1. Create ShortUrl Entity: When a long URL is provided, a new ShortUrl entity is created with the provided long URL.

  2. Save Entity: The entity is saved using the repository (urlRepo.save(shortUrl)), which automatically generates a unique ID for the record.

  3. Generate Hashed ID: We generate a hashed ID by converting the generated ID into a hexadecimal string using the hashId() method.

  4. Update Entity: The generated hashed ID is set for the entity. The entity is then updated in the database with this hashed ID.

  5. Return Shortened URL: Finally, the service returns a shortened URL that includes the hashed ID (e.g., https://urlshortner/hashedId).

getShortUrl(String shortUrl)

  1. Extract Hashed ID: When a shortened URL is requested, the hashed ID is extracted from the URL string using the getHashedIdFromShortUrl() method.

  2. Retrieve Long URL: This hashed ID is used to retrieve the corresponding long URL from the database by calling urlRepo.getByHashedId(hashedId).

hashId(Long id)

  • Convert ID to Hashed Format: This method converts the generated ID into a hashed format, such as a hexadecimal string, to create a short and unique identifier for the URL.
package com.sid121212.url_shortner.controller.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sid121212.url_shortner.ShortUrl;
import com.sid121212.url_shortner.controller.repo.JpaRepo;

@Service
public class UrlServiceImpl implements UrlService {
	
    @Autowired
    private JpaRepo urlRepo;

    @Override
    public String getShortUrl(String shortUrl) {
        String hashedId = getHashedIdFromShortUrl(shortUrl);
        return urlRepo.getByHashedId(hashedId);
    }

    private String getHashedIdFromShortUrl(String shortUrl) {
        int lastSlashIndex = shortUrl.lastIndexOf("/");
        if (lastSlashIndex != -1 && lastSlashIndex + 1 < shortUrl.length()) {
            return shortUrl.substring(lastSlashIndex + 1);
        }
        return null;
    }

    @Override
    public String saveUrl(String longUrl) {
        ShortUrl shortUrl = new ShortUrl(longUrl);
        ShortUrl savedUrl = urlRepo.save(shortUrl);

        // Generate a hashed ID based on the saved entity's generated ID
        String hashedId = hashId(savedUrl.getId());

        // Update the entity with the hashed ID and save it again
        savedUrl.setHashedId(hashedId);
        urlRepo.save(savedUrl);
        return "https://urlshortner/" + hashedId;
    }

    private String hashId(Long id) {
        return Long.toHexString(id);  // Convert ID to hex as an example
    }
}

URL Shortener API Endpoints

1. Create Shortened URL

Endpoint: POST /url

Description: This endpoint receives a long URL and returns a shortened URL.

Request Body:

  • Content-Type: application/json
  • Body:
    {
      "longUrl": "https://example.com/very-long-url"
    }
**Response**:

- **Status Code**: `200 OK`
- **Body**:
  ```json
  "https://example.com/abcd1234"

2. Retrieve Long URL

Endpoint: GET /url

Description: This endpoint retrieves the original long URL from a given shortened URL.

Query Parameters:

  • shortUrl: The shortened URL whose original long URL is to be retrieved.

Example Request:

  • URL: /url?shortUrl=https://urlshortner/abcd1234

Response:

  • Status Code: 200 OK
  • Body:
    "https://example.com/very-long-url"

With this URL shortener service, you can easily create short links and retrieve long URLs, you can also add checks like if there is repetative long url then no need to create another shortUrl and so on. Try integrating it into your projects for efficient URL handling!