Since about a month I’m developing some microservices using Javalin, a small, simple and lightweight web-framework that started as a fork from SparkJava. By now it’s a ground-up rewrite, influenced by the Javascript framework koa.js. Don’t let the stuff like Either scare you, we use the Arrow library to make our life a bit more easy :)

When requesting user input from a form, it’s evident that we need to validate our input. Javalin has its own way of doing so (from the docs):

// validate a json body:
val myObject = ctx.bodyValidator<MyObject>()
  .check({ it.myObjectProperty == someValue, "Oops, an error!" })

Resulting in the output:

"Request body as MyObject invalid - Oops, an error!"

As you see, not a lot of customisation to alter your error. So when I got the request to return an error in another format to satisfy our front-end application:

{ "fieldname": "invalid",
  "fieldname": "required",

I had to find another solution. This is where Valiktor came to the rescue, using it is easy.

Add the dependency to your application

<!-- -->

Alter the (translation) messages to your needs

For example create the file: ./src/main/resources/


Create your custom validator class

class FormValidator {
  companion object Factory {

    fun validateInput(input: UserData) : Either<BadState, UserData> =
      try {
        Either.Right(validate(input) {
      } catch (ex: ConstraintViolationException) {
         Either.Left(ValidationError( ex.constraintViolations
         .mapToMessage(baseName = "messages", locale = Locale.ENGLISH)
         .map { "${}: ${it.message}" } ))

The BadState class

sealed class BadState
data class NotAuthorised(val reason : String) : BadState()
data class OtherError(val reason : String) : BadState()
// This is the custom bad state we need :)
data class ValidationError (val errors : List<String>) : BadState()

Then in your controller:

  fun handleYourPost(ctx: Context) {
    when (val result =  createUser(ctx.body<UserData>())) {
      is Either.Right -> {
      is Either.Left -> {
        when (val error = result.a) {
          is ValidationError -> {
          is OtherError -> {
             ctx.json("something went wrong...")

  fun createUser(userInput: UserData): Either<Any, UserData> {
    return Either.fx<BadState, UserData>{
      val u = FormValidator.validateInput(userInput).bind()  //Do field validation

So now we’re happy to see the 400 error showing all fields and their errors in a json array.

{ "surname": "required",
  "email": "invalid"