Skip to main content
validate() returns a ValidationResult. It holds every violation, in the order the rules are declared, and says whether there is any:

The parts of a violation

String
Where the value is. A property (email), a nested one (shipping.zipCode), an element (items[1]) or an entry of a map (prices[USD]). It is empty for a rule about the whole object, such as exactlyOneOf without a path.
String
The message for people, with its placeholders filled in: must be at least 1, not must be at least {min}. It follows the configured messages, so it can be in your users’ language.
String
The stable name of the rule, for programs. A built-in constraint uses its own name (min, email, cpf), and a custom one the code you give it, or an empty code when you give none. Codes do not change with the messages.
Map<String, Any?>
The entries attached with metadata, and the fields of the presence rules. Empty when there are none.
Violation has value equality, so two violations with the same four parts are equal, which keeps tests short.

Turning violations into a response

A violation is plain data, so mapping it to what your API returns is ordinary Kotlin. Grouped by path, the messages are ready for a form:
And with the codes, a front end can pick its own text, translated on the client:
Use the code, not the message, to decide anything in code: messages change with translations and wording, codes do not.