1. Avaje Validator

Bean validation via Avaje Validator.

1.1. Usage

1) Add the dependency:

Maven
Gradle
<dependency>
  <groupId>io.jooby</groupId>
  <artifactId>jooby-avaje-validator</artifactId>
  <version>3.4.1</version>
</dependency>

2) Configure annotation processor

Maven
Gradle
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>...</version>
      <configuration>
        <annotationProcessorPaths>
          <path>
            <groupId>io.avaje</groupId>
            <artifactId>avaje-validator-generator</artifactId>
            <version>2.2</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

3) Install

Java
Kotlin
import io.jooby.avaje.validator.AvajeValidatorModule;

{
  install(new AvajeValidatorModule());
}

4) Usage in MVC routes

Java
Kotlin
import io.jooby.annotation.*;
import jakarta.validation.Valid;

@Path("/mvc")
public class Controller {

  @POST("/validate-body")
  public void validateBody(@Valid Bean bean) {                 (1)
    ...
  }

  @POST("/validate-query")
  public void validateQuery(@Valid @QueryParam Bean bean) {    (2)
    ...
  }

  @POST("/validate-list")
  public void validateList(@Valid List<Bean> beans) {          (3)
    ...
  }

  @POST("/validate-map")
  public void validateMap(@Valid Map<String, Bean> beans) {    (4)
    ...
  }
}
1 Validate a bean decoded from the request body
2 Validate a bean parsed from query parameters. This works the same for @FormParam or @BindParam
3 Validate a list of beans. This also applies to arrays @Valid Bean[] beans
4 Validate a map of beans

4) Usage in in script/lambda routes

Java
Kotlin
import io.jooby.validation.BeanValidator;

{
  use(BeanValidator.validate());
  post("/validate", ctx -> {
    Bean bean = ctx.body(Bean.class);
    ...
  });
}

Please note, if you are mixing both approaches (MVC/scripts), it’s better to avoid using a filter, as it may lead to double validation on MVC routes. In this case, it’s recommended to use the handler version (see below).

BeanValidator.validate() behaves identically to validation in MVC routes. It also supports validating list, array, and map of beans.

There is a handler version of it, so you can apply per route:

validate
import io.jooby.validation.BeanValidator.validate;

{
  post("/validate", validate(ctx -> {
    Bean bean = ctx.body(Bean.class);
    ...
  }));
}

1.2. Constraint Violations Rendering

AvajeValidatorModule provides default built-in error handler that catches ConstraintViolationException and transforms it into the following response:

JSON:
{
  "title": "Validation failed",
  "status": 422,
  "errors": [
    {
      "field": "firstName",
      "messages": [
        "must not be empty",
        "must not be null"
      ],
      "type": "FIELD"
    },
    {
      "field": null,
      "messages": [
        "passwords are not the same"
      ],
      "type": "GLOBAL"
    }
  ]
}

It is possible to override the title and status code of the response above:

{
  install(new AvajeJsonbModule());
  install(new AvajeValidatorModule()
    .statusCode(StatusCode.BAD_REQUEST)
    .validationTitle("Incorrect input data")
  );
}

If the default error handler doesn’t fully meet your needs, you can always disable it and provide your own:

{
  install(new AvajeJsonbModule());
  install(new AvajeValidatorModule().disableViolationHandler());

  error(ConstraintViolationException.class, new MyConstraintViolationHandler());
}

1.3. Manual Validation

The module exposes Validator as a service, allowing you to run validation manually at any time.

1.3.1. Script/lambda:

import io.avaje.validation.Validator;

{
  post("/validate", ctx -> {
    Validator validator = require(Validator.class);
    validator.validate(ctx.body(Bean.class));
    ...
  });
}

1.3.2. MVC routes with dependency injection:

1) Install DI framework at first.

import io.jooby.avaje.validator.AvajeValidatorModule;

{
  install(AvajeInjectModule.of());                 (1)
  install(new AvajeValidatorModule());
}
1 Avaje is just an example, you can achieve the same with Dagger or Guice

2) Inject Validator in controller, service etc.

import io.avaje.validation.Validator;
import jakarta.inject.Inject;

@Path("/mvc")
public class Controller {

  private final Validator validator;

  @Inject
  public Controller(Validator validator) {
    this.validator = validator;
  }

  @POST("/validate")
  public void validate(Bean bean) {
    Set<ConstraintViolation<Bean>> violations = validator.validate(bean);
    ...
  }
}

1.4. Configuration

Any property defined at validation will be added automatically:

application.conf
validation.fail_fast = true

Or programmatically:

import io.jooby.avaje.validator.AvajeValidatorModule;

{
  install(new AvajeValidatorModule().doWith(cfg -> {
    cfg.failFast(true);
  }));
}