Am I wrapping synchronous code in ktor/Kotlin coroutine correctly?
To start, I don't know Ktor, so what I'm going to tell is when Ktor doesn't do additional stuff, which is highly probably not the case.

Coroutines are managers for threads: they tell threads what to do and when to do it.
When you write a coroutine, everytime you come across a suspend-keyword, the thread that executes the task asks the coroutine what to do next. The coroutine can tell to continue with the task he was working on, but it can also tell the thread to do something else.

This is great if you have to work with triggers:


A database-call
A with triggers the threads
Another thread that returns something.


Instead of waiting, the coroutine can tell the thread to do something else and once it comes across the suspend-keyword to continue with the task if the trigger is fulfilled.

In your code, you introduce a side-task by adding the async-keyword.
The next thing you do is telling to wait on this side-task.
This means that apart from adding a suspend-keyword, it does nothing.

So, a coroutine is not for computation, but for waiting. However, coroutines can manage more than one thread. Giving the side-task to a side-thread and waiting for that side-thread to finish is something that coroutines are great for.

Therefor, your code could be better written as:

/**
 * A dedicated context for sample "compute-intensive" tasks.
 */
val compute = newFixedThreadPoolContext(4, "compute")

get("/"){
    val key = withContext(compute){
       //heavy computation
    }
    call.respond(key.toString())
}


The expanded example can be found here: https://ktor.io/samples/feature/async.html