How to build a Simple REST API with Kotlin and Spring Boot

Kotlin is a relatively new language for the JVM, and Spring Boot is a part of the Spring Framework on the Java Platform that, amongst other things, allows for a powerful development environment for writing web applications. Now that Kotlin has been officially approved as an Android programming language, this article will show you how you can also use it for creating a Web API using the Spring Boot framework.

Introduction to Kotlin

Kotlin Logo

Kotlin is a programming language that compiles to Java Byte Code, and runs on the Java Virtual Machine. One of the things that is emphasised by this programming language is interoperability with the rest of the Java eco-system - something that not all alternative JVM languages achieve.

Kotlin aims to be significantly more concise, and safer, than the standard Java Programming Language, allowing for much more rapid software development and a reduction in bugs in the end product.

Amongst the features that Kotlin provides, we will be seeing:

  • Data Classes - these are a way of writing Java Beans in a very simply way. They provide classes with fields, getters, setters (if applicable), equals, hashCode, toString and some extra helper methods, but with significantly less boilerplate than in Java.
  • Inline method declarations - These are a way of writing a method that does a simple computation without needing a method body. Instead you simply write it all inline.
  • Top level functions - These are simply functions that are not a part of a class, but exist on their own.
  • Pattern Matching - These are simplified alternatives to Switch statements or if/else blocks.
  • Not-Null by default - Variables in Kotlin can only have a value of null if they are declared as Nullable, so Null Pointer Exceptions are almost completely removed.

These are only the very barest of features that Kotlin brings to the table, so be sure to check out the rest that can be used.

Introduction to Spring Boot

Spring Boot

The Spring Framework allows for a powerful environment with which to produce rich Web Applications. Spring Boot is a subsystem of this environment that allows for very rapid setup and prototyping, and then gets out of the way to allow the rest of the project to evolve.

Amongst other things, it does all of the work for setting up a Web Environment, including providing the Web Server in which the Web Application will run. It also gives you the full power of the Spring Framework, allowing for all of the Dependency Injection and everything else that Spring brings to the table.

Project Setup

We are going to set this project up using Gradle as a build system. In order to do this, you will first need to ensure that Java 8 and Gradle are both installed. This tutorial will assume that these tools are already available on your system.

Once this is done, we need a Gradle build file. Put the following into build.gradle in the top of your project.

buildscript {
  ext.kotlin_version = '1.1.2-4'

  repositories {
    mavenCentral()
  }
  dependencies {
    // Needed for the 'kotlin' plugin
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    // Needed for the 'org.springframework.boot' plugin
    classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE"
    // Needed for the 'kotlin-spring' plugin
    classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"

  }
}

// Allows us to compile Kotlin files
apply plugin: 'kotlin'
// Does some extra work to set up Spring Boot.
// Specifically this gives us the "bootRun" task we will be using
apply plugin: 'org.springframework.boot'
// Allows for improved interop between Kotlin and Spring
apply plugin: 'kotlin-spring'

repositories {
  mavenCentral()
}

dependencies {
  // Kotlin Dependencies
  compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
  compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
  testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"

  // Spring Dependencies
  compile("org.springframework.boot:spring-boot-starter-web") {
    exclude module: "spring-boot-starter-tomcat"
  }
  compile "org.springframework.boot:spring-boot-starter-jetty"
  compile "org.springframework.boot:spring-boot-starter-actuator"

  // Jackson Dependencies
  compile "com.fasterxml.jackson.core:jackson-annotations"
  compile "com.fasterxml.jackson.core:jackson-core"
  compile "com.fasterxml.jackson.core:jackson-databind"
  runtime "com.fasterxml.jackson.datatype:jackson-datatype-jdk8"
  runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
  runtime "com.fasterxml.jackson.module:jackson-module-kotlin"
}

task wrapper(type: Wrapper) {
  gradleVersion = "3.5"
}

This will give us:

  • Kotlin 1.1.2-4 - the latest at time of writing
  • Spring Boot 1.5.2 - the latest at time of writing
    • Using Jetty instead of Tomcat for the Webapp Container, because it's lighter weight.
  • Jackson for JSON Serialization
  • The Kotlin Plugin for compiling Kotlin into Java Byte Code
  • The Spring Boot Plugin for building a Spring Boot project
  • The Kotlin-Spring Plugin, to ensure that Spring Interop is a bit smoother.

That should be everything we need for a simple webapp.

The kotlin-spring plugin in specific is important, because Kotlin defaults to marking all classes and methods as final, but Spring has a large usage of CGLib that only works with classes and methods that are open.

Unfortunately at this point it still won't build, because we don't actually have any code. We'll fix that in a second.

The initial Webapp

Once we've got our project file set up, we can start writing code. Spring Boot makes this step very easy. Put the following in to src/main/kotlin/tutorial/Application.kt:

package tutorial

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.Configuration

/**
 * The main entry point to the application
 */
@EnableAutoConfiguration
@Configuration
class Application

/**
 * Run the application
 * @param args The command line arguments
 */
fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}

You can already see the brevity that Kotlin brings to the table in some of this, and how much easier it is to read and understand.

Spring Boot expects that there is a main method defined. Unlike Java, Kotlin allows us to define this as a top-level method without needing to wrap it in a Class definition. This main method does nothing more than load the Spring Boot framework - using the SpringApplication class. This is unfortunate boilerplate, and is covered in more detail in the Spring Boot documentation.

(If you already know Spring and Spring Boot, you'll notice that there is no Autowiring or Component Scanning. This is because they obscure how the application is built, and make it impossible to actually trace through things.)

This is everything that we need to get a fully functional web app, including:

  • Starting Jetty up on port 8080 automatically
  • Built in support for WebJars - on /webjars
  • Built in support for Healthchecks, which can be easily extended - on /health
  • Built in Admin endpoints for getting various pieces of Admin information out of the system

For the amount of code that we've written so far, this is quite a lot.

Let's see what happens in practive now. Running the system in development mode is as simple as executing gradle bootRun, as follows:

> gradle bootRun
Starting a Gradle Daemon (subsequent builds will be faster)
:compileKotlin UP-TO-DATE
:compileJava NO-SOURCE
:copyMainKotlinClasses UP-TO-DATE
:processResources NO-SOURCE
:classes UP-TO-DATE
:findMainClass
:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.2.RELEASE)

2017-05-24 08:32:50.772  INFO 40802 --- [           main] tutorial.ApplicationKt                   : Starting ApplicationKt on coxgmbpsh.local with PID 40802 (/Users/coxg/source/me/writing/repos/kotlin-with-spring-boot/build/classes/main started by coxg in /Users/coxg/source/me/writing/repos/kotlin-with-spring-boot)
2017-05-24 08:32:50.775  INFO 40802 --- [           main] tutorial.ApplicationKt                   : No active profile set, falling back to default profiles: default
2017-05-24 08:32:50.816  INFO 40802 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@3444d69d: startup date [Wed May 24 08:32:50 BST 2017]; root of context hierarchy
2017-05-24 08:32:51.658  INFO 40802 --- [           main] org.eclipse.jetty.util.log               : Logging initialized @1372ms to org.eclipse.jetty.util.log.Slf4jLog
2017-05-24 08:32:51.730  INFO 40802 --- [           main] e.j.JettyEmbeddedServletContainerFactory : Server initialized with port: 8080
..........
2017-05-24 08:32:52.886  INFO 40802 --- [           main] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 10 ms
2017-05-24 08:32:52.908  INFO 40802 --- [           main] o.e.jetty.server.AbstractConnector       : Started ServerConnector@6bf13698{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2017-05-24 08:32:52.908  INFO 40802 --- [           main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8080 (http/1.1)
2017-05-24 08:32:52.913  INFO 40802 --- [           main] tutorial.ApplicationKt                   : Started ApplicationKt in 2.365 seconds (JVM running for 2.628)
<===========--> 85% EXECUTING
> :bootRun

Our application is up and running on http://localhost:8080, as simple as that.

Adding a REST Controller

This is a really boring application though. So far it doesn't do anything of any interest. So, lets change that.

Spring works by the concept of Controllers. A Controller is a piece of code that handles an incoming Request and produces an outgoing Response. We are going to be writing REST Controllers, which means that the default behaviour is to consume and produce JSON instead of HTML.

To start with, we're going to be only marginally more interesting and have a hard-coded response. Put the following in src/main/kotlin/tutorial/FirstController.kt:

package tutorial

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

/**
 * Our very first controller
 */
@RestController
class FirstController {
    /** The ultimate answer to life, the universe and everything */
    @RequestMapping("/answer")
    fun answer() = 42
}

And then update our Application.kt to look like this:

class Application {
    @Bean
    fun controller() = FirstController()
}

Just like that, we have a Controller. This code will:

  • Accept all requests to "/answer"
  • Produce a JSON response that is always the number "42"

Lets test it out. Start up your application again (gradle bootRun) and then using your HTTP tool of choice do the following:

GET /answer

A fully functional webapp that can accept requests and produce JSON responses, and we've written a total of 14 lines of code.

Our Controller class is identified using the @RestController annotation. Spring sees this as an indication that there are methods in this class that will handle HTTP Requests, and that the HTTP Requests should be treated as REST API calls and not HTML responses.

Our Controller method is identified using the @RequestMapping annotation. Spring sees this to know how to map requests on to the methods. In this case we are telling it the URL path to handle, leaving it to default the HTTP Method to "GET".

Response Payload

What if we want something a bit more complex than just returning a number? JSON allows us to return rich, structured data, so lets do that.

This will run ahead quite a bit, but it should all be fairly obvious how it all works.

Put the following in src/main/kotlin/tutorial/User.kt:

package tutorial

import java.time.Instant

/**
 * Representation of a User
 * @property username The username of the user
 * @property screenName The screen name of the user
 * @property email The email address of the user
 * @property registered When the user registered with us
 */
data class User(
        val username: String,
        val screenName: String,
        val email: String,
        val registered: Instant
)

And then add the following to FirstController.kt:

    /** Get the details of a user */
    @RequestMapping("/user")
    fun getUser() : User {
        val user = User(
                username = "grahamcox",
                screenName = "Graham",
                email = "grahamcox@example.com",
                registered = Instant.now()
        )
        return user
    }

Our second controller method, and this time it returns a rich JSON object. Lets see it in action:

GET /user

Correctly formatter JSON responses, even supporting Timestamps. And we've still written very little code.

However, we can make this better. This time add the following to src/main/resources/application.yml:

spring:
    jackson:
        serialization:
            indent_output: true
            write_dates_as_timestamps: false
            write_durations_as_timestamps: false

And then test it out again:

GET /user

The application.yml file is the core configuration for the system. We've just instructed the Jackson subsystem - used for handling JSON - to indent the output instead of having it all on one line, and to write the Dates and Times as ISO-8601 strings instead of timestamps. This makes it a lot easier for humans to read and understand.

Kotlin allows us to write classes representing only data - known as Data Classes - in a very concise manner. Spring, with the help of the "jackson-module-kotlin" dependency - can convert these classes to JSON, meaning that we can concentrate on the actual API without all of the extra boilerplate that Java would normally need.

Request Parameters

So far we've got a decent structure for getting data out of the system. But what about getting data in to it? We're going to need to be able to provide data to the system otherwise we can't do anything hugely useful with it.

Again, Spring makes this really easy. There is nothing special at all to do here - just the normal Spring handling and it all works correctly.

For example, let's add this new method to our FirstController.kt:


    /**
     * Perform some string manipulation on the given value
     * @param value The value to manipulate
     * @param operation The operation to perform
     */
    @RequestMapping("/string/{value}")
    fun manipulateString(@PathVariable("value") value: String,
                         @RequestParam(name = "operation", defaultValue = "none") operation: String) : String {
        return when (operation.toUpperCase()) {
            "REVERSE" -> value.reversed()
            "UPPER" -> value.toUpperCase()
            "LOWER" -> value.toLowerCase()
            else -> value
        }
    }

This controller method will take a Path Variable and a Querystring Parameter, using a default if the Querystring parameter is not present. We then manipulate the input Path Variable based on the Querystring parameter. This gives us the following behaviours:

  • GET /string/Hello -> "Hello"
  • GET /string/Hello?operation=reverse -> "elleH"
  • GET /string/Hello?operation=upper -> "HELLO"
  • GET /string/Hello?operation=lower -> "hello"

Run up the system and try it for yourself.

Request Payload

Of course, on occasion we're going to want more complicated inputs into the system. We've already shown that we can get complex JSON objects out, but what about getting them back in? Again, Spring makes this really easy to achieve, and Kotlin makes writing the code simple.

Put the following in src/main/kotlin/tutorial/NewUser.kt:

package tutorial

import com.fasterxml.jackson.annotation.JsonCreator

/**
 * Representation of a User to create
 * @property username The username of the user
 * @property screenName The screen name of the user
 * @property email The email address of the user
 */
data class NewUser @JsonCreator constructor(
        val username: String,
        val screenName: String,
        val email: String
)

And then our code in FirstController.kt this time will look like this:

    /**
     * Pretend to create a new user
     * @param user The details of the user to create
     */
    @RequestMapping(value = "/user", method = arrayOf(RequestMethod.POST))
    fun createUser(@RequestBody user: NewUser): User {
        return User(
                username = user.username,
                screenName = user.screenName,
                email = user.email,
                registered = Instant.now()
        )
    }

Jackson will automatically read the Body of the incoming HTTP Request and provide it to the @RequestBody parameter of the correct type.

This can be tested as follows:

POST /user

As before, the brevity of the "Data Classes" in Kotlin allow us to focus on what our API will look like without all of the extra noise that Java would bring to this. We have only written enough code to represent the actual fields on the API, and everything else is done for us.

Handling Errors

Things don't always go smoothly in your web applications. On the occasion that things do go wrong, we want to be able to handle it. Yet again, Spring makes this easy, and Kotlin makes it even easier still.

Spring has the concept of Exception Handlers, which are literally methods annotated with @ExceptionHandler that will be called whenever an exception of an appropriate type is thrown.

This time, we're adding the following to our FirstController.kt:

    /** Cause an error to occur */
    @RequestMapping("/raiseError")
    fun raiseError() {
        throw IllegalArgumentException("This shouldn't have happened")
    }

    /** Handle the error */
    @ExceptionHandler(IllegalArgumentException::class)
    @ResponseStatus(HttpStatus.CONFLICT)
    fun handleError(e: IllegalArgumentException) = e.message

The first of these is nothing special. It's just another controller handler that does nothing more than throw an exception.

The second of these is our Exception Handler, and it:

  • Handles every time an IllegalArgumentException is thrown in this controller class
  • Returns an HTTP 409 Conflict status code
  • Returns a body which is the error message

This can be tested as follows:

GET /raiseError

We can use the full support for response types here as we did above, so you can have rich JSON objects with error codes as well as messages if you desire.

Note as well that the @ResponseStatus annotation can be used on controller methods as well if you want a particular handler to always return something other than an HTTP 200 OK.

Summary

Kotlin is a fantastic way to leverage the power of the Java Environment with significantly improved safety and brevity of code.

Spring Boot is a fantastic way to build very powerful Web Applications very quickly, without scrimping on features.

The two of these can be used together to give a very powerful development environment with a large number of features, leveraging the full power of the JVM and the Java Ecosystem, and still allowing for a very efficient development process.

This tutorial only covers the very basics of interoperating with Spring Boot and Kotlin, so make sure that you read up on both of them to see how much more you can do with them.