What You Will Learn

  • Setting up Kotlin with Gradle
  • Creating a simple REST API using Javalin
  • Deploying and running the API on an embedded Jetty running on a Raspberry Pi

The instructions for this tutorial will be used with IntelliJ IDEA. It is recommended to download the free community edition of IDEA.

Setting up Kotlin with Gradle (in IntelliJ IDEA)

  • File -> New -> Project
  • Gradle -> Kotlin/JVM
  • Set the project name and other attributes of the project
  • Create src/main/kotlin/com/org/example/Main.kt

Kotlin offers a main with and without args, in our case we don’t need args so we can define main as:

fun main() {

}

Setting Up Javalin Dependencies

First, you will need to change the Kotlin dependency from implementation to compile which will be necessary later for building the runnable jar.

You will need to add jcenter to the respositories that dependencies are included in:

repositories {
    mavenCentral()
    jcenter()
}

Then, add these dependencies as well:

compile 'io.javalin:javalin:3.10.1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.10.3' // Necessary for serializing JSON
compile 'com.fasterxml.jackson.module:jackson-module-kotlin:2.10.3' // Necessary for serializing JSON 
compile 'org.slf4j:slf4j-simple:1.7.30' // Necessary to view logging output

Setting Up Javalin

We need to create the Javalin app with the IP address and port to be used on the Raspberry Pi:

fun main() {
    val ipAddress = "0.0.0.0" // change this to your Raspberry Pi's IP address
    val app = Javalin.create().apply {
        exception(Exception::class.java) { e, _ -> e.printStackTrace() }
    }.start(ipAddress, 8080)
}

The error handling isn’t necessary but helps when debugging issues that may occur with the RaspberryPi.

Adding Data To Play Around With

We are going to add a simple data class to test with:

data class StringData(val id: Long, val data: String)

Then create a DAO (Data Access Object) to allow for handling of server data

class StringDao {

    val strings: MutableList<StringData> = mutableListOf() // Server starts with empty list
    
    fun addStringData(data: StringData) {
        strings.add(data)
    }
    
    fun removeStringData(stringId: Long): Boolean = strings.removeIf { it.id == stringId }
}

Creating Simple REST API To Test With

Now we can add the REST API with our app in main:

fun main() {
    val stringDao = StringDao()

    val ipAddress = "0.0.0.0" // change this to your Raspberry Pi's IP address
    val app = Javalin.create().apply {
        exception(Exception::class.java) { e, _ -> e.printStackTrace() }
    }.start(ipAddress, 8080)

    app.routes {
        get("/strings") { context ->
            context.json(data.strings)
        }

        post("/strings") { context ->
            val stringData = context.body<StringData>()
            stringDao.addStringData(stringData)
            context.status(201)
        }

        delete("/strings/:stringId") { context ->
            stringDao.removeStringData(context.pathParam("stringId").toLongOrNull() ?: -1).let {
                if (it)
                    context.status(204)
                else
                    context.status(400)
            }
        }
    }
}

Building A Fat Jar

A fat jar is a single file which contains all the compiled Java classes and dependencies required to run an application.

To create our fat jar we need to add a custom task at the bottom of our build.gradle file:

task fatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'com.org.example.MainKt' // Make sure this includes Kt at the end of your main class
    }
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

Notice the configurations.compile bit, this is why compile was required before, so all dependencies are found and included.

This gradle task can now be run and the fat jar will be built in YourProject/build/libs/yourproject-yourversion.jar

Sending and Running On Raspberry Pi

This part of the tutorial is going to assume that we are running our Raspberry Pi in headless mode (there is no display or control devices connected to it and we are running from a different device. This is also done with a Mac terminal (ZSH) and should be identical on a Linux machine, Windows users will need to find an equivalent in Powershell.

The first step is to get the IP address of your Raspberry Pi from before. Then open a terminal and run scp yourproject-yourversion.jar [email protected]/Downloads which will create an encrypted file transfer to the Downloads folder on your Raspberry Pi. You should then be prompted for a password and the transfer will begin.

Next, SSH into the Raspberry Pi using ssh [email protected] and enter the password. At this point you should make sure that Java has been installed.

cd into Downloads and run java -jar yourproject-yourversion.jar and the server should start running.

Communicating With the REST API

Open up a separate terminal so that we can send requests to the Raspberry Pi.

To create a string, use:

curl --header "Content-Type: application/json" --request POST --data '{ "id": "1", "string": "First String"}' your.raspberry.pi.ipaddress:8080/strings

Once that has successfully gone through, you can query the strings by using:

curl your.raspberry.pi.ipaddress:8080/strings

and the response should be:

[{"id":1,"string":"First String"}]%

Finally, deleting a string can be done using:

curl --request DELETE your.raspberry.pi.ipaddress:8080/strings/1

Conclusion

I had my Raspberry Pi just sitting around a decided a fun project would be to create a REST API to play around with and learn, there are many other tutorials on this site which can be used to extend this example.