Deploying a Kotlin App to Heroku
Tags: java, kotlin, architecture
Published: 2021-10-13 11:24:52

Since its earliest release, Java has touted itself as a "[write once, run everywhere](https://en.wikipedia.org/wiki/Write_once,_run_anywhere)" programming language. The idea was that a programmer could develop an app in Java, have it compiled down to bytecode, and become an executable that can run on any platform, regardless of operating system or platform. It was able to do so in part by a runtime known as the Java Virtual Machine, or [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine). 

To Java's credit, the JVM was (and still is!) an incredibly fine-tuned runtime that abstracted away a computer's underlying hardware. While Java as a programming language survives to this day, it is often viewed as cumbersome, stodgy, and representative of an outdated approach to implementing solutions. 

In the last 10 years, more and more languages that run on the JVM have developed, but look and feel nothing like Java. One such language is [Kotlin](https://en.wikipedia.org/wiki/Kotlin_(programming_language)). Because of the JVM, it has no real performance advantages over regular Java. Still, its strength lies in the fact that it prioritizes legibility in a way that Java does not. Consider, for example, printing a substring in Java:

```
// Java
String input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42";
String answer = input.substring(input.indexOf("?") + 1);
System.out.println(answer);
```

You must first get the index of the character you want to be in the substring, add one (since strings are zero-indexed), and call `System.out.println` to write to stdout.

In Kotlin, this is much shorter:

```
// Kotlin
val input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42"
val answer = input.substringAfter("?")
println(answer)
```

Kotlin has garnered so much interest that[ Google even recommends it over Java for developing Android apps](https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/). 

In this post, we'll take a quick look at how to develop an app in Kotlin. We'll build a simple API with a PostgreSQL database and deploy it to Heroku to see it live.

## Prerequisites

Before we begin, you'll need to make sure you've got the following software installed on your machine: 

1. [An account on Heroku](https://signup.heroku.com/). This is completely free and doesn't require any payment information.
2. [The Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli#download-and-install). Once your application is on Heroku, this will make managing it much easier.
3. You'll need to have [Kotlin](https://kotlinlang.org/docs/command-line.html) installed (>= 1.4).
4. You'll also need [Gradle](https://gradle.org/install/) installed (>= 7.0).

You will also need to be a little familiar with [Git](https://git-scm.com) and have it installed on your machine.

We’re going to be using [the IntelliJ](https://www.jetbrains.com/help/idea/get-started-with-kotlin.html) IDE for this Kotlin app. Their documentation provides some guidance on how to create a new project. Make sure you select the following options:


* We want to create a Kotlin application that uses Gradle as a build system
* Set the name of the project to kotlin-api
* Set the JDK version to 16. If you don’t have this version installed, you can select **Download JDK**… from the dropdown, then choose **Oracle Open JDK** version 16

After the IDE sets everything up, you should have a directory structure that looks roughly like this:

```
kotlin-api
├── build.gradle.kts
└── src
    ├── main
    │   ├── kotlin
```

Our Kotlin files will be kept in `src/main/kotlin`, and our build logic will be in _build.gradle.kts_.


## Getting started

Gradle is a build tool for a variety of languages. It also acts as a dependency management tool, similar to Maven. You’ll already have some lines in your _build.gradle.kts_ file, which the IDE automatically added to be helpful. You can delete all of that, and paste in these lines instead:

```
plugins {
    id("java")
    id("org.jetbrains.kotlin.jvm") version "1.5.10"
    id("org.springframework.boot") version "2.4.3"

    id("io.spring.dependency-management") version "1.0.11.RELEASE"
}

group "com.example"
version "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib") 

    implementation("org.springframework.boot:spring-boot-starter-web")    
    implementation("org.springframework.boot:spring-boot-starter")

    developmentOnly("org.springframework.boot:spring-boot-devtools")
}
```

These lines specify our project's dependencies and where to find them. For example, we want to use  `[org.springframework.boot](https://plugins.gradle.org/plugin/org.springframework.boot)` at version 2.4.3, which is why it's defined within the `plugins` block. We point out the repositories where the packages can be found—at `mavenCentral()`—and which exposed classes we want to use (`implementation( "org.springframework.boot:spring-boot-starter-web")`).

Let's create two small files to test our setup. Create a file called `Application.kt` in the _src/main/kotlin_ folder and paste in the following:

```
package com.example

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
open class Application

fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}
```

This starts a default app using the Spring framework. The real magic happens in this next file, `Controller.kt`, which you should create alongside `Application.kt` in _src/main/kotlin_:

```
package com.example

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController

@RestController
class GreetingController {

    @GetMapping("/{name}")
    fun get(@PathVariable name: String) = "Hello, $name"
}
```

Here, we define a route (`@GetMapping("/{name}")`), where `{name}` is a dynamic value. By placing this decorator over a Kotlin method (`fun get`, or "a function named get"), we're able to match the route to whatever behavior we want—in this case, returning a greeting with the path name for a `GET` request. 

In order for the IDE to know how to launch our application, we need to create [a run configuration](https://www.jetbrains.com/help/idea/run-debug-configuration.html). At the top of the IDE menu, click the button that says **Add Configuration…**. Select **Add new run configuration**, then choose Gradle from the list. For the Gradle project name, enter kotlin-api. In the Tasks field, type `bootRun`. <code>[bootRun](https://docs.spring.io/spring-boot/docs/2.5.0/gradle-plugin/reference/htmlsingle/#running-your-application)</code> is a Gradle task provided by the Spring framework which will compile our code and start the server. Click <strong>Ok</strong>; you should now have a green Play button in your IDE menu bar. When you click on this, the IDE will execute  <code>gradle bootRun</code> to build this Kotlin app and start the server. When that finishes, navigate to <code>http:<em>//localhost:8080/world</em></code>. You should see a nice greeting.

## Interacting with the database

Now, let's get to the (somewhat) serious stuff. Suppose we wanted to attach a database to this project. In a Maven/Java world, we'd need to update an XML file and add a reference to a JAR file. In Gradle, we can get by with just adding a few lines to our `build.gradle.kts` file:

```
dependencies {
    # ...

    implementation("com.zaxxer:HikariCP:4.0.3")
    runtimeOnly("org.postgresql:postgresql")

    # ...
}
```

Here, we've included [HikariCP](https://github.com/brettwooldridge/HikariCP) in our project, which is a popular database connection driver. We also indicate that we want to "load" the `org.postgresql` library during runtime. With just these two lines, we've let our configuration know that we want to interact with a PostgreSQL database. If you already have a PostgreSQL database running locally, that's great. You'll be able to continue the rest of this guide locally and see the results when browsing localhost. If you don't have PostgreSQL, don't fret—we'll show you just how easy it is to deploy this app on Heroku, which will take care of the infrastructure for you. 

Head back to `Controller.kt`, and replace it with the contents below. This takes some of what we had from before but adds to it. We'll go over the changes shortly.

```
package com.example
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.http.MediaType
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import java.net.URI
import javax.sql.DataSource

@RestController
class GreetingController {

   val dataSource = dataSource()
   val connection = dataSource.connection

   @GetMapping("/{name}")

   fun get(@PathVariable name: String) = "Hello, $name"

   @PostMapping(value = ["/add-name"], consumes = [MediaType.TEXT_PLAIN_VALUE])
   fun post(@RequestBody requestBody: String) : String {
       initDb()
       val stmt = connection.createStatement()
       stmt.executeUpdate("INSERT INTO names values('$requestBody')")
       return "Added $requestBody"
   }

   @GetMapping("/everyone")

   fun getAll() : String {
       initDb()
       val stmt = connection.createStatement()
       val rs = stmt.executeQuery("SELECT name FROM names")
       val output = ArrayList<String>()
       while (rs.next()) {
           output.add(rs.getString("name"))
       }
       val names = output.joinToString(", ")
       return "Here are the names: $names"
   }

   internal fun initDb() {
       val stmt = connection.createStatement()
       stmt.executeUpdate("CREATE TABLE IF NOT EXISTS names (name text)")
   }

   internal fun dataSource(): HikariDataSource {
       val config = HikariConfig()
       var dbUri = URI(System.getenv("DATABASE_URL") ?: "postgresql://localhost:5432/")
       var dbUserInfo =  dbUri.getUserInfo()
       var username: String?; var password: String?;
       if (dbUserInfo != null) {
           username = dbUserInfo.split(":").get(0)
           password = dbUserInfo.split(":").get(1)
       } else {
           username = System.getenv("DATABASE_USERNAME") ?: null
           password = System.getenv("DATABASE_PASSWORD") ?: null
       }
       if (username != null) {
           config.setUsername(username)
       }
       if (password != null) {
           config.setPassword(password)
       }
       val dbUrl = "jdbc:postgresql://${dbUri.getHost()}:${dbUri.getPort()}${dbUri.getPath()}"
       config.setJdbcUrl(dbUrl)
       return HikariDataSource(config)
   }
}
```

There's quite a lot going on here! Let's start from the bottom. We define a function called `dataSource` which provides a connection to our database. Because we're building[ a 12-Factor app](https://12factor.net/config), our database credentials are stored in an environment variable called `DATABASE_URL`. We fetch that URL and pull out the username and password from it if one exists. If not, we check another two environment variables for that information—`DATABASE_USERNAME` and `DATABASE_PASSWORD`. We then put all that information together into a format that the database connector needs. The `initDb` function creates a table called `names`, with a single text column called `name`. The `/everyone` endpoint has a `@GetMapping` decorator just like before. This defines a `GET /everyone` route, which gets all the names from the database.

Finally, we've added something rather new: a `@PostMapping` decorator. Here, we need to define what types of content this `POST` route can accept. In this case, it `consumes` a `TEXT_PLAIN_VALUE` media type (in other words, `"Content-Type: text/plain"`). Whatever string of information we put in the request body will be added to the database. In just a few lines, we've built a small API that we can add to and query.

If you start this server now—and if you have PostgreSQL running locally—you should be able to interact with it. Try making the following request:

```
$ curl -H "Content-Type: text/plain" -X POST http://localhost:8080/add-name -d 'Frank'
```

If you navigate to `http://localhost:8080/everyone`, you'll see that `Frank` was included.

## Deploying to Heroku

It's time to see just how easy it is to get Kotlin running on Heroku. First, we need to create a file that's specific to Heroku:[ the Procfile](https://devcenter.heroku.com/articles/procfile). This text file defines how our application should boot and run. 

Create a file named _Procfile_ in the root level directory, right next to your _build.gradle.kts_ file. Copy-paste the following lines into it:

```
web: java -jar build/libs/kotlin-api.jar --server.port=$PORT
```

Here, we're saying that we want Heroku to run `java -jar build/libs/kotlin-api.jar`. That JAR is packaged and built during the deployment process; Heroku will create it automatically for us because it knows how to execute the Gradle task to do so. We are also binding the `$PORT` environment variable so that Heroku knows which port the server is listening to.

Next, run the following Git commands:

```
$ git init
$ git add .
$ git commit -m "Preparing my first Kotlin app"
```

Since we have the Heroku CLI installed, we can [call heroku create on the command line to generate an app](https://devcenter.heroku.com/articles/creating-apps):

```
$ heroku create
Creating app... done, ⬢ desolate-plains-67007
Created http://desolate-plains-67007.herokuapp.com/ | git@heroku.com:desolate-plains-67007.git
```

Your app will be assigned a random name—in this example, it's `desolate-plains-67007`—as well as a publicly accessible URL.

In order to provision a database, we use the <code>[heroku addons](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-addons-all-app-app)</code> command. Calling it without arguments will let us know if any exist:

```
$ heroku addons
No add-ons for app desolate-plains-67007.
```

No add-ons exist for our app, which makes sense—we just created it! To add a PostgreSQL database, we can use the `addons:create` command like this:

```
$ heroku addons:create heroku-postgresql:hobby-dev
```

Heroku offers several tiers of PostgreSQL databases. `hobby-dev` is the free tier, so we can play around with this without paying a dime.

Your code is ready, your Heroku app is configured—you’re ready to deploy. This is the easy part! Just type out the following command:

```
$ git push heroku master
```

Your code will be pushed to Heroku. From that point on, Heroku will take over. You'll see your build logs scrolling through your terminal. This will show you what Heroku is installing on your behalf and where you are in the build process. After it’s complete, you can visit your special URL in the browser (in this case, `https://desolate-plains-67007.herokuapp.com`) and interact with the API on the internet!

## Learning more

Kotlin's [performant design and legible syntax](https://blog.heroku.com/rise-of-kotlin)—combined with the ease of Gradle—make it ideal for enterprises that need to rely on battle-tested Java packages. Because of its interoperability with Java, Kotlin is ideal as a transitional language; vast swaths of Java code can be converted into Kotlin while still maintaining a functional app. Deploying to Heroku is smooth, and I didn't even take advantage of the different ways to [fine-tune Java and JVM-based apps for deployment](https://devcenter.heroku.com/categories/java-support).