Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Getting Started With Javalin

DZone 's Guide to

Getting Started With Javalin

In this post, we’ll create a small hello world application with Javalin based on Gradle Kotlin and then test of our application.

· Web Dev Zone ·
Free Resource

With the popularity of Spring Boot and the great support for integrating with different types of frameworks, it’s often hard to imagine using something different than Spring. However, there are numerous frameworks out there which provide similar features for building applications like microservices, and Javalin is one of those frameworks. This post will help you to get started with Javalin.

What Is Javalin?

Javalin is a web framework for building Java and Kotlin web applications in a similar fashion to frameworks like Node.js (see Koajs.com) or SparkJava, which is not surprising, since the author is one of the original SparkJava authors.

Creating a Gradle Build for Javalin

In this post, we’ll create a small hello world application based on Gradle Kotlin, including the testing of our application.

To get started with Javalin, we need to have a Javalin dependency. An easy way to do that is to use Gradle. Assuming you have Gradle installed, we can create a new project with the following command:

gradle init --type java-library --dsl kotlin

(Answer the prompted questions, their values aren’t really important for the purpose of this guide.)

Once the project has been initialized, open the build.gradle.kts file, and replace the contents with the following content:

// build.gradle.kts
plugins {
    val kotlinVersion = "1.3.21"
    id("org.jetbrains.kotlin.jvm") version kotlinVersion
    application
}

application {
    // Define the main class for the application and add Kt for Kotlin classes
    mainClassName = "HelloWorldKt"
}

dependencies {
    compile("io.javalin:javalin:2.8.0")
    compile("org.slf4j:slf4j-simple:1.7.26")

    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
}

repositories {
    jcenter()
}

The main library used here is the Javalin 2.8.0 library, which at the time of writing this piece is the latest one. After this, navigate to src/main/kotlin, and create a HelloWorld.kt file. This file will contain the contents of our complete application.

// src/main/kotlin/HelloWorld.kt
import io.javalin.Javalin

fun main() {
    val app = Javalin.create().start(7000)
    app.get("/") { ctx -> ctx.result("Hello World") }
}

Now, running gradlew run from a terminal will start up the Javalin application, running on port 7000. Opening http://localhost:7000 in your browser will result in the root route being called (app.get("/")), which will display the Hello World text in your browser. Now you’re ready to include more functionality into your app and to write a test for it, which I’ll dive into in the next section.

Testing Javalin

After creating our initial application, we will dive into a way to easily create integrations tests. We'll use the latest version of JUnit 5, plus we’ll use Fuel HTTP to test our endpoint.

What Is Fuel HTTP?

Fuel HTTP is one of the easiest networking libraries for Kotlin and quite actively developed. Fuel HTTP provides great support for handling standard HTTP calls out of the box. There are also modules which support other frameworks such as GSON, Jackson, RxJava, and many more. This makes Fuel HTTP well suited for testing REST endpoints, which is what we’ll test in this post.

Our Application Overview

We'll enhance our existing application we wrote before. We'll adapt it so it retrieves persons by invoking an HTTP GET call with an id parameter to a person endpoint, such as /api/person/1, where 1 is the id of the person. The result of this will be a JSON structure representing the person, as can be seen below:

{"name":"Yvonne","age":29}

The total solution consists of the following:

  • The Gradle build file to manage our dependencies.
  • The application configuration including the routing.
  • The person domain, including a controller, a DTO and a repository.
  • The actual integration tests.

The Gradle Build File

// build.gradle.kt
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    val kotlinVersion = "1.3.21"
    id("org.jetbrains.kotlin.jvm") version kotlinVersion
    application
}

tasks.withType<Test> {
    useJUnitPlatform()
}

dependencies {
    api("io.javalin:javalin:2.8.0")

    implementation("org.slf4j:slf4j-simple:1.7.26")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")

    testImplementation("org.junit.jupiter:junit-jupiter:5.4.1")
    testImplementation("com.github.kittinunf.fuel:fuel:2.0.1")
    testImplementation("com.github.kittinunf.fuel:fuel-jackson:2.0.1")
}

repositories {
    jcenter()
}

application {
    // Define the main class for the application
    mainClassName = "ApplicationKt"
}

As you can see in the Kotlin Gradle build file above, we've changed the build file to include our production and test dependencies. Some of the dependencies we want to expose are defined as API dependencies. The implementation details are hidden from anyone having a dependency on our application. This is done by using the implementation dependencies. Furthermore, we also have our Fuel and JUnit 5 test include testImplementation dependencies, which are used to execute our integration tests.

Application Configuration and Routing

// Application.kt
import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.get
import io.javalin.apibuilder.ApiBuilder.path
import person.PersonController
import person.personRepository

fun main() {
    JavalinApp(7000).init()
}

class JavalinApp(private val port: Int) {

    fun init(): Javalin {

        val app = Javalin.create().apply {
            port(port)
            exception(Exception::class.java) { e, _ -> e.printStackTrace() }
        }.start()

        val personController = PersonController(personRepository)

        app.get("/api/person/:id", personController::getPerson)

        return app
    }
}

In the above, we've defined our Javalin app. It's extracted to a separate class to make it easier to invoke it from the test. The Javalin class is responsible for managing the running application, which includes setting up the controller and the API routes.

Person Domain Model

// person.Person.kt
package person

import io.javalin.Context

class PersonController(private val data: Map<Int, Person>) {

    fun getPerson(ctx: Context) {
        ctx.pathParam("id").toInt().let {
            data[it]?.let { item ->
                ctx.json(item)
                return
            }
            ctx.status(404)
        }
    }
}

data class Person(val name: String, val age: Int)

// In-memory repository
val personRepository = hashMapOf(
        0 to Person("Dmitry", 37),
        1 to Person("Yvonne", 29),
        2 to Person("Peter", 52)
)

The above code, inspired by one of the Javalin tutorials, has been merged together into one file. This isn't necessarily a great idea, but for the purpose of our post, this was easier. It contains a controller which is using our injected repository, a person class, and the in-memory repository itself containing our dummy persons.

Integration Tests

// PersonIntegrationTest.kt
package person

import JavalinApp
import com.github.kittinunf.fuel.core.FuelManager
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.jackson.responseObject
import io.javalin.Javalin
import org.junit.jupiter.api.*
import org.junit.jupiter.api.Assertions.assertEquals

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisplayName("Person API")
class PersonIntegrationTest  {

    private lateinit var app: Javalin

    @BeforeAll
    fun setUp() {
        app = JavalinApp(8000).init()
        // Inject the base path to no have repeat the whole URL
        FuelManager.instance.basePath = "http://localhost:${app.port()}/"
    }

    @AfterAll
    fun tearDown() {
        // Stops the application when the tests have completed
        app.stop()
    }

    @Test
    fun `should get result for existing person`() {
        // Deconstructs the ResponseResult
        val (_, _, result) = "api/person/0".httpGet().responseObject<Person>()

        // Get the actual value from the result object
        assertEquals(personRepository[0], result.get())
    }

    @Test
    fun `should get error for non-existing person`() {
        val (_, _, result) = "api/person/-1".httpGet().responseObject<Person>()
        // Deconstructs the ResponseResult to get the FuelError
        val (_, error) = result

        assertEquals(404, error!!.response.statusCode)
    }
}

And now, after setting up our production infrastructure, this is our end result. It's an integration test which is testing two scenarios: a scenario in which there is a result found and a scenario which results into an error.

In the above code, we're using the synchronous version of Fuel HTTP, which is blocking until there is a result. Then we destructure the result into separate fields and extract the ones we need.

Executing the test using gradlew test or using your favorite IDE will result in the following test report:

Person API
+-- should get error for non-existing person   [PASSED]
+-- should get result for existing person      [PASSED]

While the example above is quite limited and in no way covers all the aspects of REST endpoint, it hopefully sets you up to start testing Javalin applications!

Topics:
kotlin ,javalin ,gradle ,web dev ,kotlin tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}