HTTP Client UI with Kotlin and JavaFX
Tags: kotlin, java, gradle, ui
Published: 2023-08-05 10:25:01

Sometimes it can be useful to use some UI to perform HTTP requests and not just CLI tools. Od course, there are tools like Postamn or Insomnia and they are great, but it can be good to have some very simple own UI to do this and test some simple thinsgs :)

Code on Github: https://github.com/ahndmal/http-client-ktl-jfx.

We will use Kotlin (https://kotlinlang.org) and OpenJFX (https://openjfx.io) and Gradle to build the app (though javac / kotlinc can be enough).

We add "org.openjfx.javafxplugin" Gradle plugin to [build.gradle.kts](https://github.com/ahndmal/http-client-ktl-jfx/blob/master/build.gradle.kts) and add basic configuration:

```gradle
plugins {
    kotlin("jvm") version "1.9.0"
    application
    **id("org.openjfx.javafxplugin") version "0.0.13"**
}

javafx {
    setVersion("20")
    setModules(listOf("javafx.controls"))
}

group = "com.andmal"
version = "0.0.2"

repositories {
    mavenCentral()
}

kotlin {
    jvmToolchain(20)
}

application {
    mainClass.set("Main")
}

```
Also, we create **Main.kt** file to add the basic logic to initiate UI with JavaFX library.


![JavaFX UI elements structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7q5i34vsgp32tkyo37m0.png)


```kotlin
class Main : Application() {
    override fun start(primaryStage: Stage?) {
        val scene = Scene(createContent(), 1200.0, 900.0)
        primaryStage?.title = "HTTP Client"
        primaryStage?.scene = scene
        primaryStage?.show()
    }
}
```

The **Main** class extends the **javafx.application.Application** and implements the **start** method to initiate the UI. We add the **createContent()** method as the parameter to start method and add the basic logic to it:

```kotlin
private fun createContent(): Parent {
   // todo
}
```

![Basic UI window](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zteee1a82qbw0fpamqem.png)

To combine the UI we can use basic [VBox](https://fxdocs.github.io/docs/html5/#_vbox_and_hbox) / HBox classes for vertical and horizonal alignment of elemtns and also [GridPane](https://fxdocs.github.io/docs/html5/#_gridpane) to map the items by rows and columns.

We add basic UI elements:

```kotlin
val mainBox = VBox()
val requestBox = VBox()
val reqPane = GridPane()
val reqBody = TextArea()

// response 
val responsePane = GridPane()
val responseBox = VBox()
val urlLabel = Label("URL")
urlField = TextField()
```
Add some padding and background:
```kotlin
mainBox.padding = Insets(10.0)
requestBox.border = Border.stroke(Paint.valueOf("orange"))
HBox.setMargin(requestBox, Insets(10.0))
reqBody.minWidth = 500.0
reqBody.minHeight = 200.0
```

Then we add buttons, headers to the core elements:

``` kotlin
reqPane.add(methodType, 0, 2)
reqPane.add(urlField, 1, 2)
reqPane.add(reqButton, 2, 2)
reqPane.add(reqHeaderButton, 2, 3)

requestBox.children.add(reqPane)
responseBox.children.add(responsePane)

mainBox.children.addAll(
    requestBox, responseBox
)
```

Add listeners to buttons to react to clicking on it:

```kotlin
reqButton.setOnMouseClicked { event ->
     if (urlField.length == 0) {
           infoHeader.text = "Error: please fill in URL"
           infoHeader.background = Background.fill(Paint.valueOf("red"))
           infoHeader.textFill = Paint.valueOf("white")
           infoHeader.font = Font.font(17.0)
           responsePane.add(infoHeader, 0, 0)
            } else {
                // correct request data
             responseBox.children.removeAll()
     }
}
```
Also, we add "Save file" button to save the response as a file:
```kotlin
saveAsFileBtn.setOnMouseClicked {
    val file = 
Files.createFile(Path.of("./response_${Instant.now().epochSecond}"))
                    file.writeBytes(httpResponse.body().toByteArray())
}
```

We use java.net.http.CLient (available since Java v.11) as simple and basic HTTP client:

```kotlin
// HTTP client
val client = HttpClient.newBuilder()
      .version(HttpClient.Version.HTTP_1_1).build()

// request
val req = HttpRequest.newBuilder()
   .uri(URI.create(urlField.text))
```

We want to have some text field for body when using POST or PUT methods:

```kotlin
methodType.setOnAction {
    when (methodType.value.first) {
       "GET" -> reqPane.children.remove(reqBody)
       "POST" ->  reqPane.add(reqBody, 1, 3)
       "PUT" ->  reqPane.add(reqBody, 1, 3)
    }
}
```

Complete building HTTP request depending on what HTTP method we select:
```kotlin
when(methodType.value.second) {
   "GET" -> req.GET()
   "POST" -> req.POST(BodyPublishers.ofString(reqBody.text))
   "PUT" -> req.PUT(BodyPublishers.ofString(reqBody.text))
   "DELETE" -> req.DELETE()
}
```

So we have request section with:
1. Select of reqest type
2. URL field
3. Send button to send request

Response:

1. Text area to show response body
2. Headers and Status buttons to show additional data when clicking them

Run the app:

![Complete UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ak7z048jpi5in2cbqn4p.png)