This is a follow up to my previous post on getting setup with Grails. In this post we are going to write a url shortening service in grails based on requirements from educative (Credits to them for giving all the details). On the way, we will discuss the different ways of achieving things in grails and solve the problem . So the problem statement is as follows
- Given a URL, our service should generate a shorter and unique alias of it.
- When users access a short link, our service should redirect them to the original link
- Users should optionally be able to pick a custom short link for their URL.
- Links will expire after a standard default time span. Users should be able to specify the expiration time.
The above requirement is implemented in the below github repository
Remember that we are only going to write the API's for these and all interactions to the system will be based on API's. Here is how i approached the problem.
Create the Empty Controller
The first step is to create a restful controller using the scaffolding method. For creating API backend applications do as follows
grails create-app --profile=rest-api
The above command will generate a simple restful application without GSP (Grails server pages). When you run the application using grails run-app
you will see that a default page with a json is rendered. Remember that the default generated app will not have a restful controller. There are two ways of generating a controller. One is writing a rest API, and the other is writing a rest API primarily for CURD operation on a resource. We are doing the former, where we write a general API that can do things beyond CURD. To do that we will run the following command
grails create-restful-controller UrlShortner
The above command will generate two files
- A restful controller
- An associated Integration test.
Now we are ready to implement our logic.
Routing
Once you have the controller ready. You need to setup the routing, the act of mapping an URI to a controller action. It took some time to understand the conventions, but once done it's pretty straight forward. The Routing is handled via a file called UrlMappings.groovy
which will be under the /grails-app/controllers/
folder. My requirement is to setup two routing entries.
- User gives a long URL and asks for a short one.
- User gives a short URL and expects it to be redirected to the original URL.
So to achieve the above i added the following two entries
post "/$controller(.$format)?"(action:"save")
get "/u/$id"(controller: 'urlEntry', action: 'redirect')
The first one is the API endpoint to get a short url given a long url. It basically relies on the ControllerName, save action and the HTTP method POST. So if there is a URL which matches https://localhost:8080/$controllerName
with a POST
method, then the save()
action will be called on the $controllerName
. In our case the UrlEntryController will have the save method implemented.
Similarly the second API is the one which does the redirect. Since the redirect URL should be short i hardcoded the /u/
as the route after the base path. This will call the redirect()
method in the urlEntry
controller.
Now when run, we are able to hit the endpoints and they hit the corresponding action in the respective controller and now the next step is to implement the logic for url shortening and redirecting.
Implementing the Logic and Data Model
+-------+ +-------------+ +---------+ +---------+
| user | | controller | | service | | domain |
+-------+ +-------------+ +---------+ +---------+
| | | |
| /post urlEntry | | |
|---------------------->| | |
| | | |
| | generateUrl(longUrl) | |
| |-------------------------->| |
| | | |
| | | generateUrl |
| | |------------ |
| | | | |
| | |<----------- |
| | | |
| | | persistGeneratedUrl() |
| | |--------------------------->|
| | | |
| /get shortUrl | | |
|---------------------->| | |
| | | |
| | getLongUrl(shortUrl) | |
| |-------------------------->| |
| | | |
| | | getFromDb(shortUrl) |
| | |--------------------------->|
| | | |