- Implement Google/GitHub Oauth
- Avoid unlogged users to use the /short-urls endpoint after X times (rate limit)
- Implement rate limit for logged users too
- Postgres cronjob for removing expired public urls
- Check for unexpected changes in the backend. What happens if someone change the input type in the date field?, disabled buttons, etc
- Avoid sending the userId to the front-end (model). Get it from the authenticationService when deleting a user
- Unit tests
- Integration tests
- Increase number of characters to the original url (~1000)
- Image carrousel explaining the product
- Add links to my accounts
- Add metadata in layout
- Check responsiveness and styles
- Improve logging and errorMessages
- Fix discrepancy between days of expiration
- Delete user account and remove all URLs linked to it
- Delete all URLs when removing a user from Admin Dashboard
- Improve logging messages
- Improve GlobalErrorHandler
- OPTIONAL:
- Button for deleting all expired urls in my-urls/admin html page
- Button for reactivating all expired urls in my-urls page
A URL shortening service built with Spring Boot, Java 21, and PostgreSQL.
This application allows users to shorten long URLs and track usage statistics. It supports both public and private URLs with user management capabilities.
- Java 21
- Maven
- Docker (PostgreSQL)
- Spring Boot 3.5.7
The system is based on two main entities:
User: Manages registered user informationShortUrl: Stores shortened URLs and their metadata
Flyway is used to manage database migrations in a controlled and versioned manner. This tool allows:
- Maintaining a history of changes to the database structure
- Applying migrations consistently across different environments
- Avoiding conflicts in database schemas
Flyway requires the following directory structure:
resources
|-- db
|-- migration
|-- V1__script_name.sql
|-- V2__script_name.sql
|-- ...
Migration scripts follow a naming convention: V{number}__{description}.sql.
To avoid the N+1 Select problem (where an additional query is performed for each retrieved record), the application sets:
spring:
jpa:
open-in-view: falseThis setting:
- Closes the EntityManager when the transaction ends (typically at the end of the controller method)
- Prevents database connections from remaining open during the entire HTTP response
- Prevents lazy loading outside the transaction context
- Improves performance by releasing database resources more quickly
- Forces explicitly loading all necessary data within the transaction
In order to use this approach, the relationships have to be marked with fetch = FetchType.LAZY and retrieve the necessary data
directly from the query (either using psql join columns or using @EntityGraph annotation), resulting in a single executed query by Spring Data JPA and not N+1.
The application uses Thymeleaf as a template engine with Bootstrap to provide a responsive and modern interface.
This is the recommended approach as it orchestrates all services together.
If you want to run the Spring Boot application from your IDE and only use Docker for the database:
- Start only the PostgreSQL service:
docker-compose up -d linkshade-db- Run the Spring Boot application from your IDE or using Maven:
./mvnw spring-boot:run- Stop the database when finished:
docker-compose downTo run both the database and the application as containers:
- Build and start all services in detached mode (background):
docker-compose up -d --buildNote: --build can be avoided if no changes were made. In that case, the local built image will be used.
- View logs:
docker-compose logs -f- Stop all services:
docker-compose down- Stop and remove volumes (database data):
docker-compose down -v