Search code examples
kotlinerror-handlingquarkusquarkus-panachequarkus-rest-client

How do I correctly validate a REST request in Quarkus using Kotlin?


I'm currently creating an application in Quarkus, using Kotlin. I'm trying to create a simple users endpoint using RestEasy, Panache, and Hibernate. My challenge right now is that exception handling is not correctly done. I want to display a correct and understandable message to the user when the request isn't valid.

This is my UserResource for the createUser POST request:

@POST
    @Transactional
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    fun createUser(@Valid user: User) : CreateUserResponse =
        try {
            userRepository.persist(user)
            CreateUserSuccess(user)
        } catch (e: Exception) {
            CreateUserFailure(e)
        }

And this is my User entity:

@Entity
data class User (
    @Id
    @GeneratedValue(generator = "UUID")
    var id: UUID? = UUID.randomUUID(),

    @NotBlank
    var fullName: String,

    @Email
    @NotBlank(message = "email may not be blank")
    var email: String,

    @CreationTimestamp
    var createdAt: LocalDateTime? = null,

    @UpdateTimestamp
    var updatedAt: LocalDateTime? = null,
)

And for the completeness, this is my UserRepository:

@ApplicationScoped
class UserRepository : PanacheRepository<User>

Creating a user does work, when I'm sure the request is valid. But I'd also like to make sure invalid requests get handled nicely when the request isn't valid. This is the response I'm getting right now when I hit the createUser endpoint:

com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of
`com.fortuneapp.backend.application.domain.core.models.entities.User`, problem: Parameter specified as non-null is null:
method com.fortuneapp.backend.application.domain.core.models.entities.User.&lt;init&gt;, parameter email
at [Source: (io.quarkus.vertx.http.runtime.VertxInputStream); line: 3, column: 1]

What am I missing here?


Solution

  • You are having an issue with the nulatibility of your properties, as you can see even tough you are defining var properties some of them are not nullable ? so because you are deserializing a json message, the input of the rest services, Jackson is not being able to create the new instance of your model class user because some of the required fields are not present.

    This is similar to when you try to create a new object but do not provide the required arguments in the constructor.

    There is some recommendations to work with kotlin an Jax-RS and Hibernate-Panache here https://quarkus.io/guides/kotlin.

    What you are trying to archive its perfectly possible but if you want to use you Entity as request body, you must make all the fields nullable, otherwise the underlying Json serializer will not be able to create the new instance of your model to pass the @Valid validations and generate the constraint violation report.


    You have some options here.

    1. You can create a Data class with all properties nullable so you will ensure that all javax validation annotations will be able to be checked because the request will be able to be deserialized. Then map this object to your Model entity class. You can see this class as a DTO because at the end you are moving data through a layer boundary and decoupling you service contract from within you entity model implementation, which tend to be convenient.
    2. Even though the use of the javax.validation constraint annotations is quite handy, you can inject a validator and retrieve the Constraint violations and return a custom data class with the constraint violations in a more readable way, than the default report, which is not supported by quarkus in the reactive version of resteasy.

    If you have more doubts i can provide an example.