Error Handling

Elide handles errors as defined in specification of the endpoint format (JSONAPI or GraphQL). Where possible Elide will already create meaningful errors for you. In cases such as custom validations or data store related exceptions you might want to customize the behavior.

Elide builtin validations

Elide will perform classic bean validation of your entity classes by making use of the Hibernate validator. This basically covers all javax.validation annotations.

Throwing custom exceptions

Since classic bean validation can only perform a subset of validations, you might use pre-security or pre-commit lifecycle hooks to perform additional checks. If you want to give the user proper error response here, you should throw a CustomErrorException.

A CustomErrorException can contain one or multiple errors in the errorObjects field. Use the ErrorOjectsBuilder to create them.

Mapping third party exceptions into meaningful errors

In some scenarios, user interaction might cause exceptions in the application that are beyond your control. It might be desired to remap these exceptions in order to return the client meaningful error codes. This can be achieved by configuring Elide to use a custom implementation of an ErrorMapper.

Mapping ConstraintViolationExc

A common example for this case are constraint violations in the JPA datastore. If your database encounters a constraint violation hibernate will throw a (nested) ConstraintViolationException containing the name of the violated constraint.

By default Elide would throw a generic error message with the description “Could not execute statement”.

However, the name of the violated constraint can be used to derive a proper error message.

Here is an example implementation written in Kotlin:

@Component
class HibernateErrorMapper : ErrorMapper {
    override fun map(origin: Exception): CustomErrorException? {
        val error = findHibernateConstraintViolationExceptionOrNull(origin) ?: return null

        return when (error.constraintName) {
            "datasource_name_uindex" -> Errors.DATASOURCE_NAME_NOT_UNIQUE.asElideError()
            else -> null
        }
    }

    fun findHibernateConstraintViolationExceptionOrNull(exception: Exception, maxDepth: Int = 5): ConstraintViolationException? =
        if (maxDepth <= 0) {
            null
        } else {
            val cause = exception.cause
            when {
                exception is ConstraintViolationException -> exception
                cause is Exception -> findHibernateConstraintViolationExceptionOrNull(cause, maxDepth - 1)
                else -> null
            }
        }
}