Ktor: Responding Early

When a web server handles a request, it will often need to have multiple exit points—your error and success exit points are typically different. How do you do this in Ktor?

The naive approach:

fun Route.myRoute() {
    post("/doSomething") {
        if (!isValidRequest()) {
            call.respondText("Validation failed!", HttpStatusCode.BadRequest)
        }

        // Some logic here...

        call.respond("Great success!")
    }
}

There’s a problem with this: call.respond* does not “return.” Execution will continue, and the remainder of the route is run even though your validation has failed.

Respond Early

The idiomatic way to handle this is to add a return when we respond:

fun Route.myRoute() {
    post("/doSomething") {
        if (!isValidRequest()) {
            return@post call.respondText("Validation failed!", HttpStatusCode.BadRequest)
        }

        // Some logic here...

        call.respond("Great success!")
    }
}

I wish this was a bit better documented; as far as I know it only shows up on the status pages feature documentation, despite being a common use case.

If you’re curious why this is valid—the post body expects a Unit return type. Conveniently, all of the respond* methods return Unit.

Alternative

The status page feature also presents an alternate approach: throw an exception and use that to return an appropriate error code.

I’d generally avoid doing this, as you’re now using exceptions for control flow. At the very least, this seems avoidable for business logic errors. It sounds a bit more reasonable if (for example) some kind of I/O exception gets bubbled up to your route.