DEV Community

Yazan Musleh
Yazan Musleh

Posted on

How to Build a Simple TinyURL Clone

Introduction

URL shortening services have been around for quite some time now, with services like TinyURL and Bitly helping users convert long, cumbersome URLs into shorter, easy-to-share links. In this blog, I'll walk you through the process of creating your own TinyURL-like service using Spring Boot on the backend and React and TypeScript for the frontend. By the end, you'll have a functional URL shortening service to call your own!

Technologies

Backend: Java, Spring Boot, Maven
Frontend: React, TypeScript, Axios

Backend: Java and Spring Boot

To get started with our backend, we'll use Spring Boot, a popular Java framework for building web applications. Spring Boot allows us to create a RESTful API with minimal boilerplate code.

Setting up the project

  1. Create a new Spring Boot project using the Spring Initializer: https://start.spring.io/
  2. Select Java 17, Maven, and add the following dependencies: Spring Web, Lombok.
  3. Download the project and unzip it into your desired directory.

Implementing the backend logic

Our backend will have the following structure:

  • Url: A Java class representing the URL entity.
  • UrlRepository: A Java interface to manage the persistence of our URL entities.
  • UrlService: A Java class responsible for handling the business logic.
  • UrlController: A Java class responsible for handling incoming HTTP requests.

Url.java

@Entity
public class Url {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String originalUrl;
    private String shortUrl;
    // Add constructors, getters, and setters
}
Enter fullscreen mode Exit fullscreen mode

UrlRepository.java

public interface UrlRepository extends JpaRepository<Url, Long> {
    Url findByShortUrl(String shortUrl);
    Url findByOriginalUrl(String originalUrl);
}
Enter fullscreen mode Exit fullscreen mode

UrlService.java

@Service
public class UrlService {
    private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static final int SHORT_URL_LENGTH = 8;

    @Autowired
    private UrlRepository urlRepository;

    private String generateRandomShortUrl() {
        StringBuilder shortUrlBuilder = new StringBuilder(SHORT_URL_LENGTH);
        Random random = new Random();

        for (int i = 0; i < SHORT_URL_LENGTH; i++) {
            int index = random.nextInt(ALPHABET.length());
            shortUrlBuilder.append(ALPHABET.charAt(index));
        }

        return shortUrlBuilder.toString();
    }

    public Url createUrl(String originalUrl){
        String shortUrl;
        Url url;

        do {
            shortUrl = generateRandomShortUrl();
            url = urlRepository.findByShortUrl(shortUrl);
        }while (url != null);

        url = new Url();
        url.setOriginalUrl(originalUrl);
        url.setShortUrl(shortUrl);
        return urlRepository.save(url);
    }

    public Url getUrlByShortUrl(String shortUrl){
        return urlRepository.findByShortUrl(shortUrl);
    }
}
Enter fullscreen mode Exit fullscreen mode

UrlController.java

@RestController
@RequestMapping("/api")
public class UrlController {
    @Autowired
    private UrlService urlService;

    @PostMapping("/createUrl")
    public ResponseEntity<Url> createUrl(@RequestParam("url") String originalUrl) {
        try {
            new URL(originalUrl);
        } catch (MalformedURLException e) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        Url newUrl = urlService.createUrl(originalUrl);
        if (newUrl != null) {
            return new ResponseEntity<>(newUrl, HttpStatus.CREATED);
        } else {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }
    }

    @GetMapping("/{shortUrl}")
    public void redirectToOriginalUrl(@PathVariable String shortUrl, HttpServletResponse response) {
        Url url = urlService.getUrlByShortUrl(shortUrl);
        if (url != null) {
            response.setHeader("Location", url.getOriginalUrl());
            response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
        } else {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
    }

}
Enter fullscreen mode Exit fullscreen mode

Frontend: React and TypeScript

For our frontend, we'll use React, a popular JavaScript library for building user interfaces, and TypeScript, a statically typed superset of JavaScript that adds types to the language.

Setting up the project

  • Create a new React project with TypeScript in your terminal:
    npx create-react-app tinyurl-frontend --template typescript

  • Navigate to the tinyurl-frontend directory and install Axios, a library to make HTTP requests:
    npm install axios

Implementing the frontend logic

Our frontend application will have a simple form for users to enter a long URL and receive a short URL in return. We'll use Axios to make requests to our backend API.

  1. Create a new file CreateUrlForm.tsx and define a functional component to render the form.
import React, { useState } from "react";
import axios from "axios";

const CreateUrlForm: React.FC = () => {
  const [originalUrl, setOriginalUrl] = useState("");
  const [shortUrl, setShortUrl] = useState("");

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    const apiUrl = "http://localhost:8080";
    const response = await axios.post(`${apiUrl}/api/createUrl`, null, {
      params: {
        url: originalUrl,
      },
    });

    setShortUrl(response.data.shortUrl);
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <label>
          Original URL:
          <input
            type="text"
            value={originalUrl}
            onChange={(event) => setOriginalUrl(event.target.value)}
          />
        </label>
        <button type="submit">Shorten</button>
      </form>
      {shortUrl && <p>Short URL: {shortUrl}</p>}
    </div>
  );
};

export default CreateUrlForm;
Enter fullscreen mode Exit fullscreen mode
  1. Update src/App.tsx to include the CreateUrlForm component:
import React from "react";
import CreateUrlForm from "./CreateUrlForm";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>TinyURL Clone</h1>
      </header>
      <main>
        <CreateUrlForm />
      </main>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now, we have a simple frontend that allows users to shorten URLs.

Conclusion

In this blog post, we've covered how to create a simple URL shortening service using Java/Spring Boot and React/TypeScript. This application is just the beginning, and you can expand it with additional features such as custom short URLs, URL analytics, and more. With the basic foundation in place, you can continue to build and refine your application to meet your needs.

I hope you found this tutorial helpful in getting started with building your own TinyURL-like service. Good luck, and happy coding!

Top comments (0)