Vert.x When writing Vert.x verticles, everything is about non-blocking asynchronous operations, of course. Therefore if we want to have Amazon DynamoDB as our database, we want to also call it asynchronously. In this example I assume you already created the DynamoDB table.

First we start with creating the asynchronous DynamoDB client:

  private val db = AmazonDynamoDBAsyncClientBuilder
    .standard()
    .build()

Then we create our Vert.x verticle:

  class BooksVerticle : AbstractVerticle() {

      // ...

      override fun start() {
          val router = Router.router(vertx)

          router.route().handler(BodyHandler.create())
          router.post("/books").handler(this::handleBook)

          vertx.createHttpServer()
                  .requestHandler(router::accept)
                  .listen(8080)
      }

      private fun handleBook(event: RoutingContext) {
         // TODO
      }
  }

The book is being POSTed as JSON, so to write it to DynamoDB we call PutItemAsync:

  val item = Item()
          .withPrimaryKey("id", UUID.randomUUID().toString())
          .withJSON("json", event.bodyAsString)

  db.putItemAsync("books", InternalUtils.toAttributeValues(item), object : AsyncHandler<PutItemRequest, PutItemResult> {

      override fun onError(exception: kotlin.lang.Exception) {
          event.response()
                  .putHeader(HttpHeaders.CONTENT_TYPE, "text/plain")
                  .setStatusCode(500)
                  .end(exception.message)
      }

      override fun onSuccess(request: PutItemRequest, result: PutItemResult) {
          event.response().setStatusCode(201).end()
      }
  })

To make it even better, we can rewrite this to using coroutines instead. Coroutines is a new feature in Kotlin that allows for cleaning up some of these callbacks.

To have a nice integration between Kotlin coroutines and Vert.x, there is an additional Vert.x module you must add to your Gradle dependencies:

  compile "io.vertx:vertx-lang-kotlin-coroutines:$vertx_version"

Read more about it here: http://vertx.io/docs/vertx-lang-kotlin-coroutines/kotlin/

Then you must extend CoroutineVerticle instead of AbstractVerticle:

  class BooksVerticle : CoroutineVerticle() {

    // ...

    suspend override fun start() {
        // ...
    }
  }

Note that start has now become a suspending function. From this function you can call other suspending functions.

If we look at PutItemAsync, it requires a com.amazonaws.handlers.AsyncHandler as a callback. But actually we really want to use awaitResult provided by the Vert.x Kotlin Coroutines integration module, which requires a different callback: io.vertx.core.Handler. Therefore we define our own coroutine that bridges the two:

  suspend fun <Req : AmazonWebServiceRequest, Res> awaitAmazonWS(block: (ah: AsyncHandler<Req, Res>) -> Unit): Res {
    return awaitResult { h ->
        block.invoke(object : AsyncHandler<Req, Res> {

            override fun onError(exception: kotlin.lang.Exception) {
                h.handle(Future.failedFuture(exception))
            }

            override fun onSuccess(request: Req, result: Res) {
                h.handle(Future.succeededFuture(result))
            }
        })
    }
  }

Now we can use that to rewrite our handler:

  private fun handleBook(event: RoutingContext) {
        launch(context.dispatcher()) {
            try {
                val item = Item()
                        .withPrimaryKey("id", UUID.randomUUID().toString())
                        .withJSON("json", event.bodyAsString)

                awaitAmazonWS<PutItemRequest, PutItemResult> { h ->
                    db.putItemAsync("books", InternalUtils.toAttributeValues(item), h)
                }

                event.response().setStatusCode(201).end()
            } catch (e: Exception) {
                event.response()
                        .putHeader(HttpHeaders.CONTENT_TYPE, "text/plain")
                        .setStatusCode(500)
                        .end(e.message)
            }
        }
   }

Note that you must wrap a handler body in the launch block, because you want to invoke a coroutine, but handlers themselves cannot be suspending. This is described here: http://vertx.io/docs/vertx-lang-kotlin-coroutines/kotlin/#_handlers.

Using coroutines with Vert.x allows you to simplify a lot of callbacks to just code with standard exception handling. Much more readable!