
Compile-time generation of type-safe, reflection-free validators from annotations, producing readable code with fail-fast, sensitive-data masking, conditional rules, schema export and framework adapters.
Compile-time generated validation logic for Kotlin. Zero reflection. Generated Kotlin code.
Valix uses Kotlin Symbol Processing (KSP) to generate type-safe validators at compile time—delivering reflection-free validation with zero runtime overhead and zero cold-start delay.
T?), and data classes.iosArm64, iosX64, iosSimulatorArm64), Web (JS), and WebAssembly (Wasm).package com.example.user
import io.valix.annotations.*
data class CreateUserRequest(
@NotBlank
val username: String,
@Email
val email: String,
@Min(18)
val age: Int,
@Sensitive(mask = "[REDACTED]")
@MinLength(8)
val password: String
)val request = CreateUserRequest(username = "john", email = "invalid", age = 15, password = "123")
val result = CreateUserRequestValidator.validate(request)
if (!result.valid) {
result.errors.forEach { error ->
println("${error.field}: ${error.message} (Rejected: ${error.rejectedValue})")
}
}KSP generates standard, human-readable Kotlin procedural code in your build/generated/ksp/ folder:
public object CreateUserRequestValidator : ValixValidator<CreateUserRequest> {
override fun validate(value: CreateUserRequest, vararg groups: KClass<out Any>, failFast: Boolean): ValidationResult {
val errors = mutableListOf<ValidationError>()
val usernameVal = value.username
if (usernameVal.trim().isEmpty()) {
errors.add(ValidationError(field = "username", code = "NOT_BLANK", message = "must not be blank", path = "username"))
if (failFast) return ValidationResult(false, errors)
}
val emailVal = value.email
if (!emailVal.matches(EMAIL_REGEX)) {
errors.add(ValidationError(field = "email", code = "EMAIL_INVALID", message = "invalid email", path = "email"))
if (failFast) return ValidationResult(false, errors)
}
val ageVal = value.age
if (ageVal < 18) {
errors.add(ValidationError(field = "age", code = "MIN_VALUE", message = "must be at least 18", path = "age"))
if (failFast) return ValidationResult(false, errors)
}
val passwordVal = value.password
if (passwordVal.length < 8) {
errors.add(ValidationError(field = "password", code = "MIN_LENGTH", message = "minimum length is 8", rejectedValue = "[REDACTED]", path = "password"))
if (failFast) return ValidationResult(false, errors)
}
return ValidationResult(errors.isEmpty(), errors)
}
}| Feature | Valix | Bean Validation (JSR 380) | Valiktor | Konform |
|---|---|---|---|---|
| Kotlin-First Design | Yes | No (Java-centric) | Yes | Yes |
| Execution Model | KSP Codegen | Runtime Reflection | Runtime Reflection | Type-safe DSL |
| Reflection-Free | Yes | No | No | Yes |
| Kotlin Multiplatform (KMP) | Yes (JVM, iOS, JS, Wasm) | No | No | Yes |
| Spring Boot / Ktor / Micronaut | Yes (Dedicated Adapters) | Yes (Spring default) | Manual | Manual |
| Jetpack Compose Integration | Yes | No | No | No |
| OpenAPI / JSON Schema Export | Yes (Built-in Generator) | Ecosystem Addons | No | No |
| Fail-Fast Execution Mode | Yes | No | No | No |
| Async Validator Codegen | Yes | No | No | No |
Microbenchmarks executed via Java Microbenchmark Harness (JMH) comparing Valix against Hibernate Validator (the reference JSR-380 implementation):
| Case | Hibernate Validator (JSR-380) | Valix | Throughput Speedup |
|---|---|---|---|
| Invalid Payload Validation | 874,809 ops/sec | 7,866,714 ops/sec | ~9.0x speedup |
| Valid Payload Validation | 905,822 ops/sec | 8,511,063 ops/sec | ~9.4x speedup |
Bypassing runtime reflection and annotation introspection yields ~9.4x higher operational throughput with zero cold-start latency.
For complete integration projects and real-time validation execution benchmarks of Valix across Spring Boot, Micronaut, Ktor, Kotlin Multiplatform (KMP), and Android Jetpack Compose, check out the dedicated examples repository:
👉 DeveloperSyndicate/Valix-Examples
plugins {
kotlin("jvm") version "2.3.21"
id("com.google.devtools.ksp") version "2.3.9"
}dependencies {
// Core annotations and runtime
implementation("com.developersyndicate.valix:valix-core:1.0.5")
implementation("com.developersyndicate.valix:valix-runtime:1.0.5")
// KSP annotation processor
ksp("com.developersyndicate.valix:valix-ksp:1.0.5")
}valix-spring): Auto-configures SpringMessageResolver to translate message keys using native Spring MessageSource localizations and handles controller parameter validation.valix-ktor): Pipeline interceptor validating incoming call request payloads automatically.valix-micronaut): AOP advice (@ValixValidated) and method interceptor for parameter validation.valix-compose): State management via rememberValixForm() and ValidatedTextField.valix-flow): Reactive stream validation operator (validateWith).valix-viewmodel): ViewModel state binding via ValixFormViewModel.Terminate validation execution immediately on the first encountered error to reduce unnecessary processing:
val result = CreateUserRequestValidator.validate(request, failFast = true)Redact sensitive inputs from error reporting:
data class LoginRequest(
@NotBlank
val username: String,
@Sensitive(mask = "[REDACTED]")
@MinLength(8)
val password: String
)Evaluate constraints on a property only when sibling properties satisfy condition checks:
data class PaymentRequest(
val paymentType: String,
@ValidateIf(field = "paymentType", equals = "CARD")
@NotBlank(message = "Card number is required for card payments")
val cardNumber: String?
)Expose constraint parameters (min, max, value) directly inside error message templates:
data class Account(
@MinLength(value = 8, message = "Minimum length is {min}")
val username: String
)Validate third-party or domain models without adding annotations:
val UserValidator = valixDsl<DomainUser> {
field("email", DomainUser::email) {
notBlank()
email()
}
field("age", DomainUser::age) {
min(18)
}
}@NotNull, @NotBlank, @Email, @MinLength(val), @MaxLength(val), @Pattern(regex), @Url, @PhoneNumber, @Alpha, @AlphaNumeric, @LowerCase, @UpperCase, @Contains(val), @StartsWith(val), @EndsWith(val).
@Min(val), @Max(val), @Range(min, max), @Positive, @PositiveOrZero, @Negative, @NegativeOrZero.
@NotEmpty, @Size(min, max), @AllowedValues(array).
Because Valix generates direct procedural Kotlin code at build time, it performs zero runtime reflection:
-keep rules for your validated data classes or validator classes.consumer-rules.pro file is required.Comprehensive documentation is available in the docs/ directory:
@Sensitive, @ValidateIf, failFast, schema export, and valixDsl.docs/LLMS.txt: High-density context summary for AI coding assistants.docs/LLMS_FULL.txt: Complete API and integration reference for LLM context ingestion.Valix is open-source software licensed under the Apache 2.0 License.
Compile-time generated validation logic for Kotlin. Zero reflection. Generated Kotlin code.
Valix uses Kotlin Symbol Processing (KSP) to generate type-safe validators at compile time—delivering reflection-free validation with zero runtime overhead and zero cold-start delay.
T?), and data classes.iosArm64, iosX64, iosSimulatorArm64), Web (JS), and WebAssembly (Wasm).package com.example.user
import io.valix.annotations.*
data class CreateUserRequest(
@NotBlank
val username: String,
@Email
val email: String,
@Min(18)
val age: Int,
@Sensitive(mask = "[REDACTED]")
@MinLength(8)
val password: String
)val request = CreateUserRequest(username = "john", email = "invalid", age = 15, password = "123")
val result = CreateUserRequestValidator.validate(request)
if (!result.valid) {
result.errors.forEach { error ->
println("${error.field}: ${error.message} (Rejected: ${error.rejectedValue})")
}
}KSP generates standard, human-readable Kotlin procedural code in your build/generated/ksp/ folder:
public object CreateUserRequestValidator : ValixValidator<CreateUserRequest> {
override fun validate(value: CreateUserRequest, vararg groups: KClass<out Any>, failFast: Boolean): ValidationResult {
val errors = mutableListOf<ValidationError>()
val usernameVal = value.username
if (usernameVal.trim().isEmpty()) {
errors.add(ValidationError(field = "username", code = "NOT_BLANK", message = "must not be blank", path = "username"))
if (failFast) return ValidationResult(false, errors)
}
val emailVal = value.email
if (!emailVal.matches(EMAIL_REGEX)) {
errors.add(ValidationError(field = "email", code = "EMAIL_INVALID", message = "invalid email", path = "email"))
if (failFast) return ValidationResult(false, errors)
}
val ageVal = value.age
if (ageVal < 18) {
errors.add(ValidationError(field = "age", code = "MIN_VALUE", message = "must be at least 18", path = "age"))
if (failFast) return ValidationResult(false, errors)
}
val passwordVal = value.password
if (passwordVal.length < 8) {
errors.add(ValidationError(field = "password", code = "MIN_LENGTH", message = "minimum length is 8", rejectedValue = "[REDACTED]", path = "password"))
if (failFast) return ValidationResult(false, errors)
}
return ValidationResult(errors.isEmpty(), errors)
}
}| Feature | Valix | Bean Validation (JSR 380) | Valiktor | Konform |
|---|---|---|---|---|
| Kotlin-First Design | Yes | No (Java-centric) | Yes | Yes |
| Execution Model | KSP Codegen | Runtime Reflection | Runtime Reflection | Type-safe DSL |
| Reflection-Free | Yes | No | No | Yes |
| Kotlin Multiplatform (KMP) | Yes (JVM, iOS, JS, Wasm) | No | No | Yes |
| Spring Boot / Ktor / Micronaut | Yes (Dedicated Adapters) | Yes (Spring default) | Manual | Manual |
| Jetpack Compose Integration | Yes | No | No | No |
| OpenAPI / JSON Schema Export | Yes (Built-in Generator) | Ecosystem Addons | No | No |
| Fail-Fast Execution Mode | Yes | No | No | No |
| Async Validator Codegen | Yes | No | No | No |
Microbenchmarks executed via Java Microbenchmark Harness (JMH) comparing Valix against Hibernate Validator (the reference JSR-380 implementation):
| Case | Hibernate Validator (JSR-380) | Valix | Throughput Speedup |
|---|---|---|---|
| Invalid Payload Validation | 874,809 ops/sec | 7,866,714 ops/sec | ~9.0x speedup |
| Valid Payload Validation | 905,822 ops/sec | 8,511,063 ops/sec | ~9.4x speedup |
Bypassing runtime reflection and annotation introspection yields ~9.4x higher operational throughput with zero cold-start latency.
For complete integration projects and real-time validation execution benchmarks of Valix across Spring Boot, Micronaut, Ktor, Kotlin Multiplatform (KMP), and Android Jetpack Compose, check out the dedicated examples repository:
👉 DeveloperSyndicate/Valix-Examples
plugins {
kotlin("jvm") version "2.3.21"
id("com.google.devtools.ksp") version "2.3.9"
}dependencies {
// Core annotations and runtime
implementation("com.developersyndicate.valix:valix-core:1.0.5")
implementation("com.developersyndicate.valix:valix-runtime:1.0.5")
// KSP annotation processor
ksp("com.developersyndicate.valix:valix-ksp:1.0.5")
}valix-spring): Auto-configures SpringMessageResolver to translate message keys using native Spring MessageSource localizations and handles controller parameter validation.valix-ktor): Pipeline interceptor validating incoming call request payloads automatically.valix-micronaut): AOP advice (@ValixValidated) and method interceptor for parameter validation.valix-compose): State management via rememberValixForm() and ValidatedTextField.valix-flow): Reactive stream validation operator (validateWith).valix-viewmodel): ViewModel state binding via ValixFormViewModel.Terminate validation execution immediately on the first encountered error to reduce unnecessary processing:
val result = CreateUserRequestValidator.validate(request, failFast = true)Redact sensitive inputs from error reporting:
data class LoginRequest(
@NotBlank
val username: String,
@Sensitive(mask = "[REDACTED]")
@MinLength(8)
val password: String
)Evaluate constraints on a property only when sibling properties satisfy condition checks:
data class PaymentRequest(
val paymentType: String,
@ValidateIf(field = "paymentType", equals = "CARD")
@NotBlank(message = "Card number is required for card payments")
val cardNumber: String?
)Expose constraint parameters (min, max, value) directly inside error message templates:
data class Account(
@MinLength(value = 8, message = "Minimum length is {min}")
val username: String
)Validate third-party or domain models without adding annotations:
val UserValidator = valixDsl<DomainUser> {
field("email", DomainUser::email) {
notBlank()
email()
}
field("age", DomainUser::age) {
min(18)
}
}@NotNull, @NotBlank, @Email, @MinLength(val), @MaxLength(val), @Pattern(regex), @Url, @PhoneNumber, @Alpha, @AlphaNumeric, @LowerCase, @UpperCase, @Contains(val), @StartsWith(val), @EndsWith(val).
@Min(val), @Max(val), @Range(min, max), @Positive, @PositiveOrZero, @Negative, @NegativeOrZero.
@NotEmpty, @Size(min, max), @AllowedValues(array).
Because Valix generates direct procedural Kotlin code at build time, it performs zero runtime reflection:
-keep rules for your validated data classes or validator classes.consumer-rules.pro file is required.Comprehensive documentation is available in the docs/ directory:
@Sensitive, @ValidateIf, failFast, schema export, and valixDsl.docs/LLMS.txt: High-density context summary for AI coding assistants.docs/LLMS_FULL.txt: Complete API and integration reference for LLM context ingestion.Valix is open-source software licensed under the Apache 2.0 License.