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.3.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.1</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);
    ...
  });
}

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);
  }));
}