Building URL Shortening Service frontend - P4

In this blog, we are going to learn to build a form using Kotlin.HTML to frontend and using the existing APIs.

Subscribe to my newsletter and never miss my upcoming articles

This is the last part of the series(an optional one). In this one, you are going to build a basic form using HTML using Kotlin.HTML template engine.

This is going to be a very rudimentary blog of building a frontend without any style (like CSS). It would be a very basic HTML form.

In this blog, you are going to learn,

  • Setting up Kotlin.HTML in the project.
  • Setting up the project
  • Building an HTML form using Kotlin.HTML.
  • Submitting the form to a dedicated API route.
  • Final Steps

Setting up Kotlin.HTML in the project.

To integrate Kotlin.HTML in your project, add the following dependency to your build.gradle.kts like,

implementation("io.ktor:ktor-html-builder:$ktor_version")

This will help you to use HTML in a DSL way in the project.

Setting up the project

You have already built, the url feature in the project. For this, create a new package called frontend,

features -> frontend -> views

and in that, you are going to have views and a file called FrontendRouting.

Basically, you are going to build one new route to submit the data using the HTML form, and using that route itself, you will fetch the response as well.

Here, let us add another route in the UrlEntity as,

const val FORM_URL = "/v1/form"

And using this URL, create another Location known as FormUrlLocation which looks like,

@KtorExperimentalLocationsAPI
@Location(UrlEntity.FORM_URL)
class FormUrlLocation

You don't need to create any other use case for this as you are going to use CreateShortUrlUseCase to create your short URL.

Create two files LandingPage and ResponsePage in the views folder. Here, LandingPage will have the HTML form and ResponsePage will show the shortened URL using HTML.

Building an HTML form using Kotlin.HTML

Firstly, create an Application's extension function landingPage and inside this, you will have a routing function like,

fun Application.landingPage() {

    routing {

    }
}

You will create your Kotlin.HTML form inside the routing function with a GET call and a route like,

 routing {
        get("/") {
           //the HTML form will be rendered here
        }
    }

The above block of code means, that the default page of the base URL will open this form. For Eg. https://himashoe.com/ will open the form.

Now, let us render the form. For this, use call.respondHtml like,

 call.respondHtml {
                head {
                    title {
                        +"Welcome to URL Shortener"
                    }
                }
                body {
                    form(
                        action = UrlEntity.FORM_URL,
                        method = FormMethod.post,
                    ) {
                        label {
                            input(
                                type = InputType.url,
                                name = "url",
                            ) {
                                placeholder = "Enter your url"
                                required = true
                            }
                        }

                        button(
                            classes = "primary",
                            type = ButtonType.submit,
                        ) {
                            +"Shorten"
                        }
                    }
                }
            }

Here, inside the block, you have head and inside that, you have title which is responsible for adding that title "Welcome to URL Shortener" to the Browser tab-like

Screenshot 2021-07-05 at 12.56.09 AM.png

Then you have the body function, which renders a form with an input field and a button.

The form takes action and method as parameters where action represents the endpoint the data has to be submitted and method means the HTTP method which in your case is UrlEntity.FORM_URL and POST respectively.

In the input field, you have the type of data you need, i.e URL. You can also have other options like Number, date, etc. and other is name which is set to "url". The value of this form will be set like,

url: " the input data"

And, at last, you have a placeholder which means what to show if the form is empty and required which means it's mandatory to have data there before pressing the button.

With that being said, you have Button with type = ButtonType.submit and text on Button is Shorten.

The form looks like,

Screenshot 2021-07-05 at 1.03.14 AM.png

Submitting the form to a dedicated API route

Now, create an Application's extension function responsePage with parameters DomainProvider and ExceptionProvider like,

fun Application.responsePage(domainProvider: DomainProvider, exceptionProvider: ExceptionProvider) {

    routing {
    }
}

Inside routing, use the FormUrlLocation to create a POST request and inside that render the HTML based on the response. This FormUrlLocation acts as the API you used in the above step to submit the form. Hence, you will receive the url in this POST request.

Using the location the POST request looks like,

  routing {
        post<FormUrlLocation> {

        }
    }

Inside this, you have to receive the url parameter which you are passing from the HTML form.

To receive the parameters in the route, use:

  val params = call.receiveParameters()

and using this you can get the url params like:

val params = call.receiveParameters()
val url = params["url"] ?: ""

Here, url variable holds the url parameter coming from the form.

Now, you need to do nothing new. You will do what you did early by first validating and then passing it to the use case.

Now, the complete code of the POST request using FormUrlLocation looks like:

  post<FormUrlLocation> {
            val params = call.receiveParameters()
            val url = params["url"] ?: ""
            if (isValid(url)) {
                val response =
                    domainProvider.provideCreateShortUrlUseCase().invoke(url)
                call.respondHtml {
                    body {
                        p {
                            if (response is SuccessResponse) {
                                +"Short url is: /${response.data}"
                            }
                        }
                    }
                }
            } else {
                call.respond(
                    HttpStatusCode.BadRequest,
                    exceptionProvider.respondWithGenericException("Url is not valid!")
                )
            }
        }

Here, if you see first you are validating the url. If the URL is valid then pass it to the CreateShortUrlUseCase using the domainProvider.

If the response is successful, then render it in HTML using call.respondHtml and print it in Paragraph.

The response on the browser looks like, if you pass the https://himanshoe.com/series/short-url-service URL in the form,

Screenshot 2021-07-05 at 1.17.48 AM.png

Final steps

As the final steps, register both the routes in FrontendRouting file, like:

fun Application.frontendRouting(domainLocator: DomainProvider, provideExceptionProvider: ExceptionProvider) {

    routing {
        landingPage()
        responsePage(domainLocator, provideExceptionProvider)
    }
}

and finally register the frontendRouting in the configureRouting function like:


fun Application.configureRouting() {
    install(Locations)
    routing {
        frontendRouting(domainLocator.provideDomainProvider(), exceptionLocator.provideExceptionProvider())
        urlRoutes(domainLocator.provideDomainProvider(), exceptionLocator.provideExceptionProvider())
    }
}

Here, now in configureRouting you can see that both of your routes for URL and frontend are registered.

Rest all the things remain the same in the project.

This is all about building your own URL Shortening Service using Ktor. This is just a sample to showcase how to use the template engine in Ktor.

If you like the series, do share it with people :)

Check the code here.

Thank you for reading :) Hope you learned something from my experience.

If you have anything more to discuss, I will be happy to get connected at hi_man_shoe.

No Comments Yet