Your task is to create a URL shortener web application (similar to bitly or TinyURL). This exercise is intentionally open-ended, and you are welcome to implement your solution using the language and tech stack of your choice. If you are familiar with React & Next.js, please use those for your submission. The core functionality of the application should be expressed through your own original code.
You should aim to spend no more than 2 hours on this project. If you don't complete everything in 2 hours, please submit what you have - we value your time and want to see your prioritization skills.
At the root path (e.g., http://localhost:8080/), a user should be presented with a form that allows them to input a URL. When a user submits that form, it should convert the input URL to a shortened version and present that to the user.
The shortened URL should be in the format: http://localhost:8080/{slug}, where {slug}
is a unique identifier for the original URL.
When a user navigates to the shortened URL, they should be redirected to the original URL that was used to generate this shortened URL.
- Format and method of generating slugs for shortened URLs are up to you
- Shortened URLs do not need to persist across server shutdown/startup (i.e., setting up a DB isn't necessary - server memory should suffice)
- Only allow valid http(s) URLs
If you have additional time, consider spending it on testing or UI improvements as opposed to supplemental features.
We will be evaluating your submission based on the following:
- Functionality: Does the application work as described?
- Code quality: Is the code clean, well-organized, and following best practices?
- Error handling: How does the application handle invalid inputs or errors?
- Technical choices: Are the chosen technologies appropriate for the task?
- Documentation: Is the code well-commented and the README clear?
Please fill out the sections below in the README.md of your project and submit according to the instructions you received with this project. Your code can be sent as a zip file or a link to a repository containing your project.
- The choice for frontend was going to be React.js because it allows us to build interfaces in form of reusable components, providing more flexibility and state management capabilities.
- I chose Bootstrap 5 for styling because it is easy to use and because I've used it the most in the past.
- Instead of using the fetch API, I chose Axios for making API requests because it provides abstraction over the fetch API which makes hanlding requests easier and the code more scalable and maintainable.
- For the backend I had to decide between Node.js and Spring Boot. While node.js is a good choice for building RESTful APIs quickly, I decided to go with Spring Boot for the following reasons
- Scalability and Performance: While Node.js can handle a large number of requests through its non-blocking architecture, Spring Boot is is more scalable, performant and provides security out of the box which also makes it production ready.
- Developer Productivity: Spring Boot annotations are powerful and avoid boilerplate code, and Java being an Object oriented language helps to model entites and behaviours easily. While Typescript was an option that I considered but I find Java to be more robust and easier to work with.
- Spring boot has a slightly larger initial memory footprint compared to Node.js but I felt that the tradeoff was worth it for long term perspective.
- The libaray
JNanoId
was used to generate unique slugs for the shortened URLs. It is a Java implementation of NanoId library on NPM which is a tiny, secure unique string ID generator for JavaScript.- It claims to be better than standard UUID and it also has a collision probability calculator which I checked for 10 characters and 1M requests / hour and it was 65 days which is decent for this use case. Later I found that for 11 characters and 1M requests / hour the collision probability is 1 year which is even better.
- It also provides the flexibility to provide alphabet i.e. the set of characters using which the slug will be generated. I used the default alphabet.
- The application follows a client server architecture.
- The source code in both sub-projects is organised by feature i.e.
slug
is the feature and all files related to it are present in theslug
sub-folder in src. - The Backend is a REST API which provides the specified endpoints and one additional endpoint
GET /slugs
- To retrieve the list of all slugs and their corresponding URLs.
- CORS is enabled for all origins, for all methods and headers as of now.
- The Frontend is a React.js application which has 2 components -
URLShortnerForm
andSlugList
URLShortnerForm
is a form which takes the URL input and sends it to the backend to get the shortened URL.SlugList
is a component which displays the list of all slugs and their corresponding URLs.
- The service abstraction in both the projects handles the responsibility of hanlding the data and interacting with the persistance layer (in this case the in-memory map in the backend and the session storage in the frontend).
- I've used sessionStorage instead of localStorage to avoid persisting the data across sessions as our data is anyways in-memory as of now.
- I've followed the following branching strategy in git
main
- This branch contains production code. No direct commits allowed (by convention).release/{version}
- Created from main and only used for creating and merging child feature branches which contain code corresponding to the release version. Only this branch can be merged intomain
.feature/{feature-name}
- Created from release branch and used for developing a feature. Once the feature is complete, it is merged back to the release branch. Cannot merge directly inmain
.
- I did do the task in 2 hours but I spent additional 30 minutes (timed) to complete the Slug list component and iron out a few UI bugs.
- I planned to write unit tests for the backend but could not do so due to time constraints.
- The documentation is written outside of the 2h time window
- Co-pilot's inline suggestions were used to autocomplete simple logic and boiler plate code but the core logic was written by me.
- Java 17 or higher
- Node.js 18.x or higher
- npm 10.2.x or higher
-
Navigate to the
Backend
directory:cd Backend
-
Build the project using Gradle:
./gradlew build
-
Run the Spring Boot application:
./gradlew bootRun
The backend server should now be running at http://localhost:8080.
-
Navigate to the directory:
cd ../Frontend
-
Install the dependencies:
npm install
-
Start the development server:
npm run dev
The frontend application should now be running at http://localhost:5173.
Accessing the Application (View Demo)
- Open your browser and navigate to to access the URL shortener web application.
- You can use the form to input a URL and get a shortened version.
- The shortened URL will be in the format , where
{slug}
is a unique identifier for the original URL. - When you navigate to the shortened URL, you will be redirected to the original URL.
- The backend server runs on port
8080
by default. - The frontend development server runs on port
5173
by default. - Ensure that both the backend and frontend servers are running simultaneously to use the application.
I have performed manual testing for both the frontend and backend.
For the backend, I've used Postman to test the endpoints. You can import this Postman collection file to test the endpoints.
- VS Code (with copilot)
- Backend
- Spring Boot (with Gradle)
- Spring Web
- JNanoId (for generating unique slugs)
- Java 17
- Spring Boot (with Gradle)
- Frontend
- React.js
- Bootstrap 5 (for styling)
- Axios (for API requests)
Good luck, and we look forward to reviewing your submission!