Small Spring WebFlux library that auto-configures JSON error responses for common API failures.
It provides:
EntityNotFoundExceptionmapped to404 NOT_FOUNDValidationExceptionmapped to400 BAD_REQUEST- WebFlux binding errors and invalid enum values mapped to
400 BAD_REQUEST
dependencies {
implementation("io.github.kostack:api-errors:<version>")
}dependencies {
implementation 'io.github.kostack:api-errors:<version>'
}<dependency>
<groupId>io.github.kostack</groupId>
<artifactId>api-errors</artifactId>
<version>VERSION</version>
</dependency>The package is a Spring Boot auto-configuration module. After it is on the classpath, Spring Boot registers the default handlers automatically.
Throw EntityNotFoundException when a requested resource cannot be found:
import io.github.kostack.api_errors.exception.EntityNotFoundException
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
@RestController
class UserController(
private val users: UserRepository
) {
@GetMapping("/users/{id}")
suspend fun findUser(@PathVariable id: String): User =
users.findById(id) ?: throw EntityNotFoundException(User::class, id)
}Response:
{
"message": "Entity not found with identifier 123 of type User",
"violations": []
}Throw ValidationException for domain validation errors:
import io.github.kostack.api_errors.dto.ApiValidation
import io.github.kostack.api_errors.exception.ValidationException
fun validateCreateUser(request: CreateUserRequest) {
val violations =
buildList {
if (request.email.isBlank()) {
add(ApiValidation.Violation(fieldId = "email", message = "must not be blank"))
}
if (request.age < 18) {
add(
ApiValidation.Violation(
fieldId = "age",
message = "must be greater than or equal to 18",
metadata = mapOf("minimum" to 18)
)
)
}
}
if (violations.isNotEmpty()) {
throw ValidationException(violations)
}
}Response:
{
"message": "Validation Failed",
"violations": [
{
"fieldId": "email",
"message": "must not be blank",
"metadata": {}
},
{
"fieldId": "age",
"message": "must be greater than or equal to 18",
"metadata": {
"minimum": 18
}
}
]
}Spring WebFlux binding errors are handled automatically. For example, request DTO validation failures are returned as ApiValidation violations:
import jakarta.validation.Valid
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
data class CreateUserRequest(
@field:NotBlank
@field:Email
val email: String
)
@RestController
class CreateUserController {
@PostMapping("/users")
suspend fun createUser(@Valid @RequestBody request: CreateUserRequest): UserResponse {
TODO("create user")
}
}If an enum request value cannot be parsed, the response includes the invalid value and valid enum constants:
{
"message": "Validation Failed",
"violations": [
{
"fieldId": "status",
"message": "Invalid value 'disabled'. Valid values: ACTIVE, INACTIVE",
"metadata": {}
}
]
}The default handlers back off when a bean of the same handler type already exists:
import io.github.kostack.api_errors.handler.WebExchangeBindExceptionHandler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class ApiErrorConfiguration {
@Bean
fun webExchangeBindExceptionHandler(): WebExchangeBindExceptionHandler =
WebExchangeBindExceptionHandler()
}For different response behavior, define your own @RestControllerAdvice for the exception type you want to handle.
This project is licensed under the MIT License. See LICENSE.