In this post we'll set a foundation for Web API server written in Kotlin and then build upon it in future posts covering topics like database access, authentication and deployment.
Why Kotlin?
Kotlin is a great language for JVM that generated much buzz when it was declared to be one of the officially supported languages for Android development.
However, the main strength of Kotlin is its amazing Java interoperability story and that makes it a great choice for building backend services as well.
We could of course use one of the newest cool frameworks for Kotlin, like KTOR, but why do that when we can leverage that interoperability story to use an amazing existing Java ecosystem that has been polished for God knows how long already?
Dropwizard
One of the projects that tries to consolidate some of the better practices of web development on JVM is Dropwizard.
It includes:
- Jetty for HTTP
- Jersey for REST
- Jackson for JSON
- Logback and slf4j for logging
- JDBI and Hibernate for database access
- etc
Dropwizard itself has pretty awesome docs including getting started guide, but it's all Java and Maven, which kind of makes you feel like writing legacy code from the get go.
How about living a little and making it all Kotlin and Gradle?
New Kotlin application
We'll build a simplistic Calculator web service. The full source code is available on GitHub.
Easiest way to get started is to create a Gradle project in IntelliJ choosing a Kotlin (JVM) template.
Set GroupID: to.dev.example
, ArtifactID: kotlin-webapi
, check Use auto-import
and you are good to go. IntelliJ will setup basic project layout for compiling Kotlin code.
Add a src/main/kotlin/to/dev/example/MainApp.kt
file, it will contain our application's entry point:
package to.dev.example
fun main(args: Array<String>) {
println("Hello, Kotlin!")
}
By default IntelliJ creates a Gradle build for a library, not an application. Let's add some lines to the build.gradle
file at the root of our project to turn it into an runnable application:
apply plugin: 'application'
mainClassName = 'to.dev.example.MainAppKt'
Notice the MainAppKt
class name? Where does it come from?
There are no functions outside of classes in Java world, so Kotlin conveniently created a class for us behind the scenes, named it <FileName>Kt
and put the main
function in there.
Now we can go to the project root and type: $ ./gradlew run
to make Gradle build and run our modest application.
:compileKotlin UP-TO-DATE
:compileJava NO-SOURCE
:copyMainKotlinClasses UP-TO-DATE
:processResources NO-SOURCE
:classes UP-TO-DATE
:run
Hello, Kotlin!
Now that we have a running application, let's add some Dropwizard to it.
Dropwizard application
Dropwizard is structured as a collection of smaller libraries, so that you can add only the ones you need.
For now dropwizard-core
will be enough.
Add the following lines to the same build.gradle
file:
ext.dropwizard_version = '1.1.4'
dependencies {
compile "io.dropwizard:dropwizard-core:$dropwizard_version"
}
Configuration
Dropwizard has a notion of configuration files built into it so deep that in fact the first thing you need to do to have a running Dropwizard application is to define the configuration class derived from io.dropwizard.Configuration
.
This configuration will primarily be used to distinguish between development and production settings like database connection strings.
Kotlin makes it very easy to create such simple classes. Just one line of code:
class CalculatorConfig(val name: String = "unknown") : Configuration()
Of course we need an actual configuration file to be parsed into this config.
Add a config/local.yaml
class to the root of our project:
name: Kotlin Calculator
Application
To create a web service we will need to register components with Jersey.
Components can easily be enhanced with standard and self-explanatory javax.ws.rs
annotations:
@Path("/")
class CalculatorComponent {
@Path("/add")
@GET
fun add(@QueryParam("a") a: Double, @QueryParam("b") b: Double): Double {
return a + b
}
@Path("/multiply")
@GET
fun multiply(@QueryParam("a") a: Double, @QueryParam("b") b: Double): Double {
return a * b
}
@Path("/divide")
@GET
fun divide(@QueryParam("a") a: Double, @QueryParam("b") b: Double): Double {
if (b == .0) {
throw WebApplicationException("Can't divide by zero")
}
return a / b
}
}
Combined with your configuration declared above a class derived from io.dropwizard.Application
forms a core of your Dropwizard application and that's where we will register our component with Jersey:
class CalculatorApp : Application<CalculatorConfig>() {
override fun run(configuration: CalculatorConfig, environment: Environment) {
println("Running ${configuration.name}!")
val calculatorComponent = CalculatorComponent()
environment.jersey().register(calculatorComponent)
}
}
Note: this registers a singleton component which will be shared between requests. There are other much more powerful ways to control creation of components for serving resources, but that's a topic for another day.
Now to create an object of this class and pass it command line arguments from our main
function:
fun main(args: Array<String>) {
CalculatorApp().run(*args) // use collection as a varargs
}
Run
$ ./gradlew run
...
:run
usage: java -jar project.jar [-h] [-v] {server,check} ...
positional arguments:
{server,check} available commands
optional arguments:
-h, --help show this help message and exit
-v, --version show the application version and exit
Looks like we need to pass some command line arguments to the app.
We should build an artifact and run it as a standalone Java application, but I prefer to hack it for now providing those arguments to the run task in build.gradle
file:
run {
args = ['server', 'config/local.yaml']
}
Run again:
$ ./gradlew run
...
Running Kotlin Calculator!
...
INFO [2017-09-16 21:17:27,181] io.dropwizard.server.ServerFactory: Starting CalculatorApp
...
INFO [2017-09-16 21:17:27,664] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:
GET / (to.dev.example.CalculatorComponent)
GET /add (to.dev.example.CalculatorComponent)
GET /multiply (to.dev.example.CalculatorComponent)
...
INFO [2017-09-16 21:17:27,681] org.eclipse.jetty.server.AbstractConnector: Started application@720bf653{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
INFO [2017-09-16 21:17:27,682] org.eclipse.jetty.server.AbstractConnector: Started admin@360bc645{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
...
As you can see in the above logs web app started listening on default port 8080
and admin port 8081
:
$ curl "localhost:8080/add?a=5&b=10"
15
$ curl "localhost:8080/multiply?a=5&b=10"
50
$ curl "localhost:8080/divide?a=5&b=0"
{"code":500,"message":"Can't divide by zero"}
Voilà , we have a self-contained web service written in less than 50 lines of Kotlin code capable of serving 30,000-50,000 requests per second.
In the next posts we'll cover testing, serving JSON objects, creating a standalone app for deployment, database access and authentication.
Top comments (2)
Thanks for this article. I am looking forward to the one you tease at the end. I've been curious about Kotlin and how it can be used as a web server.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.