Easy, modular CRUD with HATEOAS HAL build for Kotlin
Find a file
Renovate Bot 771191610d
All checks were successful
/ release (push) Successful in 2m7s
fix(deps): update all dependencies (#28)
This PR contains the following updates:

| Package | Type | Update | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| postgres | service | minor | `18.1` → `18.3` | ![age](https://developer.mend.io/api/mc/badges/age/docker/postgres/18.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/docker/postgres/18.1/18.3?slim=true) |
| postgres |  | minor | `18.1` → `18.3` | ![age](https://developer.mend.io/api/mc/badges/age/docker/postgres/18.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/docker/postgres/18.1/18.3?slim=true) |
| [org.apache.maven.plugins:maven-surefire-plugin](https://maven.apache.org/surefire/) ([source](https://github.com/apache/maven-surefire)) | build | patch | `3.5.4` → `3.5.5` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.apache.maven.plugins:maven-surefire-plugin/3.5.5?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.apache.maven.plugins:maven-surefire-plugin/3.5.4/3.5.5?slim=true) |
| [org.apache.maven.plugins:maven-failsafe-plugin](https://maven.apache.org/surefire/) ([source](https://github.com/apache/maven-surefire)) | build | patch | `3.5.4` → `3.5.5` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.apache.maven.plugins:maven-failsafe-plugin/3.5.5?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.apache.maven.plugins:maven-failsafe-plugin/3.5.4/3.5.5?slim=true) |
| [org.apache.maven.plugins:maven-compiler-plugin](https://maven.apache.org/plugins/) ([source](https://github.com/apache/maven-compiler-plugin)) | build | minor | `3.14.1` → `3.15.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.apache.maven.plugins:maven-compiler-plugin/3.15.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.apache.maven.plugins:maven-compiler-plugin/3.14.1/3.15.0?slim=true) |
| [io.mockk:mockk-jvm](https://mockk.io) ([source](https://github.com/mockk/mockk)) | test | patch | `1.14.7` → `1.14.9` | ![age](https://developer.mend.io/api/mc/badges/age/maven/io.mockk:mockk-jvm/1.14.9?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.mockk:mockk-jvm/1.14.7/1.14.9?slim=true) |
| [org.junit.jupiter:junit-jupiter](https://junit.org/) ([source](https://github.com/junit-team/junit-framework)) | test | patch | `6.0.1` → `6.0.3` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.junit.jupiter:junit-jupiter/6.0.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.junit.jupiter:junit-jupiter/6.0.1/6.0.3?slim=true) |
| [io.swagger.core.v3:swagger-core-jakarta](https://github.com/swagger-api/swagger-core) | compile | patch | `2.2.41` → `2.2.45` | ![age](https://developer.mend.io/api/mc/badges/age/maven/io.swagger.core.v3:swagger-core-jakarta/2.2.45?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.swagger.core.v3:swagger-core-jakarta/2.2.41/2.2.45?slim=true) |
| [org.postgresql:postgresql](https://jdbc.postgresql.org) ([source](https://github.com/pgjdbc/pgjdbc)) | build | patch | `42.7.8` → `42.7.10` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.postgresql:postgresql/42.7.10?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.postgresql:postgresql/42.7.8/42.7.10?slim=true) |
| [org.jooq:jooq-codegen-maven](https://www.jooq.org) ([source](https://github.com/jOOQ/jOOQ)) | build | patch | `3.20.10` → `3.20.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jooq:jooq-codegen-maven/3.20.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jooq:jooq-codegen-maven/3.20.10/3.20.11?slim=true) |
| [org.flywaydb:flyway-database-postgresql](https://flywaydb.org) ([source](https://github.com/flyway/flyway)) | build | major | `11.20.0` → `12.1.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.flywaydb:flyway-database-postgresql/12.1.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.flywaydb:flyway-database-postgresql/11.20.0/12.1.0?slim=true) |
| [org.springframework.boot:spring-boot-dependencies](https://spring.io/projects/spring-boot) ([source](https://github.com/spring-projects/spring-boot)) | import | patch | `4.0.1` → `4.0.3` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.springframework.boot:spring-boot-dependencies/4.0.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.springframework.boot:spring-boot-dependencies/4.0.1/4.0.3?slim=true) |
| [org.flywaydb:flyway-maven-plugin](https://flywaydb.org) ([source](https://github.com/flyway/flyway)) | build | major | `11.20.0` → `12.1.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.flywaydb:flyway-maven-plugin/12.1.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.flywaydb:flyway-maven-plugin/11.20.0/12.1.0?slim=true) |
| [de.loosetie.logging:lt-logging-bom](https://loosetie.de) | import | patch | `1.0.9` → `1.0.10` | ![age](https://developer.mend.io/api/mc/badges/age/maven/de.loosetie.logging:lt-logging-bom/1.0.10?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/de.loosetie.logging:lt-logging-bom/1.0.9/1.0.10?slim=true) |
| [de.loosetie.utils:lt-utils-bom](https://loosetie.de) | import | patch | `2.0.0` → `2.0.2` | ![age](https://developer.mend.io/api/mc/badges/age/maven/de.loosetie.utils:lt-utils-bom/2.0.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/de.loosetie.utils:lt-utils-bom/2.0.0/2.0.2?slim=true) |
| [de.loosetie.crudular:lt-crudular-bom](https://loosetie.de) | import | major | `1.8.7` → `2.0.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/de.loosetie.crudular:lt-crudular-bom/2.0.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/de.loosetie.crudular:lt-crudular-bom/1.8.7/2.0.0?slim=true) |
| [org.jooq:jooq-meta](https://www.jooq.org) ([source](https://github.com/jOOQ/jOOQ)) | compile | patch | `3.20.10` → `3.20.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jooq:jooq-meta/3.20.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jooq:jooq-meta/3.20.10/3.20.11?slim=true) |
| [org.jooq:jooq-kotlin](https://www.jooq.org) ([source](https://github.com/jOOQ/jOOQ)) | compile | patch | `3.20.10` → `3.20.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jooq:jooq-kotlin/3.20.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jooq:jooq-kotlin/3.20.10/3.20.11?slim=true) |
| [org.jooq:jooq-codegen](https://www.jooq.org) ([source](https://github.com/jOOQ/jOOQ)) | compile | patch | `3.20.10` → `3.20.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jooq:jooq-codegen/3.20.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jooq:jooq-codegen/3.20.10/3.20.11?slim=true) |
| [org.jooq:jooq](https://www.jooq.org) ([source](https://github.com/jOOQ/jOOQ)) | compile | patch | `3.20.10` → `3.20.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jooq:jooq/3.20.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jooq:jooq/3.20.10/3.20.11?slim=true) |
| [org.postgresql:postgresql](https://jdbc.postgresql.org) ([source](https://github.com/pgjdbc/pgjdbc)) | compile | patch | `42.7.8` → `42.7.10` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.postgresql:postgresql/42.7.10?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.postgresql:postgresql/42.7.8/42.7.10?slim=true) |
| [de.loosetie.crudular:lt-crudular-parent](https://loosetie.de) | parent | major | `1.8.7` → `2.0.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/de.loosetie.crudular:lt-crudular-parent/2.0.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/de.loosetie.crudular:lt-crudular-parent/1.8.7/2.0.0?slim=true) |
| [de.loosetie:lt-parent-kotlin](https://loosetie.de) | parent | patch | `1.0.8` → `1.0.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/de.loosetie:lt-parent-kotlin/1.0.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/de.loosetie:lt-parent-kotlin/1.0.8/1.0.11?slim=true) |
| [de.loosetie:lt-parent-kotlin](https://loosetie.de) | parent | patch | `1.0.10` → `1.0.11` | ![age](https://developer.mend.io/api/mc/badges/age/maven/de.loosetie:lt-parent-kotlin/1.0.11?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/de.loosetie:lt-parent-kotlin/1.0.10/1.0.11?slim=true) |

Co-authored-by: Renovate Bot <renovate@denktmit.tech>
Co-committed-by: Renovate Bot <renovate@denktmit.tech>
2026-03-16 12:28:51 +00:00
.forgejo/workflows fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
.github Create dependabot.yml 2025-01-13 13:20:33 +01:00
_bom fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
_parent fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
api fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
base fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
docs/howto/user docs: Added first HowTos (#4) 2025-02-19 08:50:21 +00:00
openapi-base fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
repo-in-memory fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
repo-jooq fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
rest-webmvc fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
sample/spring-simple fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
security-spring fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
spring-boot fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
test fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
.editorconfig chore: moved API and base modules from another repository 2024-06-13 17:47:54 +02:00
.gitignore chore: moved API and base modules from another repository 2024-06-13 17:47:54 +02:00
docker-compose.yaml fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
LICENSE.txt docs: dependency updates 2024-10-24 12:35:20 +02:00
pom.xml fix(deps): update all dependencies (#28) 2026-03-16 12:28:51 +00:00
README.md refactor: Extracted MetaModel and RestPropertyMappers from CrudMetaModel (#11) 2025-04-04 13:04:07 +00:00
renovate.json fix(deps): update all dependencies (#5) 2025-02-22 21:05:57 +00:00

LT :: CRUDular

CRUDular is an easy, modular way to add CRUD with HATEOAS HAL to your Kotlin/Java application.

It's created mainly to ease use cases such as Master Data, or user created content. There is no silver bullet for all your requirements, so please think about your usage profile before trying to do everything with the same approach.

See sample/spring-simple for a minimal example using Spring-Boot with WebMVC and a postgres database.

Usage

Add dependencies

  1. Import the BOM which contains version for all CRUD modules.
  2. Choose the implementations for rest and repo you need. For the spring framework there are starters, with default settings.

For details of the modules take a look at their README.md

Maven


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>de.loosetie.crudular</groupId>
                <artifactId>lt-crudular-bom</artifactId>
                <version>...</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>de.loosetie.crudular</groupId>
            <artifactId>lt-crudular-repo-jooq</artifactId><!-- Choose your flavor -->
        </dependency>
        <dependency>
            <groupId>de.loosetie.crudular</groupId>
            <artifactId>lt-crudular-rest-webmvc</artifactId><!-- Choose your flavor -->
        </dependency>
    </dependencies>
</project>

Gradle

plugins {
    id 'io.spring.dependency-management' version '...' // We suggest using this plugin, even if you do not use spring
}
dependencyManagement {
    imports {
        mavenBom "de.loosetie.crudular:lt-crudular-bom:..."
    }
    dependencies {
        mavenBom "de.loosetie.crudular:lt-crudular-repo-jooq"
        mavenBom "de.loosetie.crudular:lt-crudular-rest-webmvc"
    }

Create CRUD entities

A CRUD entity is defined in 3 parts: Entity, Key and Search.

When using kotlin it's recommended to keep those classes in the same file :)

Entity

To create a CRUD entity create a class which implements HasBK and annotate it with @CrudEntity. It's recommended that such classes are immutable.

import de.loosetie.crudular.api.entity.CrudEntity
import de.loosetie.crudular.api.HasBK

@CrudEntity
data class Sample(
    var name: String,
    var something: String
) : HasBK<SampleKey> {
    override fun key() = SampleKey(author, title)
    override fun equals(other: Any?) = keyEquals(other) # generic equals using the key
    override fun hashCode() = keyHashCode()
}

Hint: If more performance id needed consider implementing hashCode and equals individually.

Key

Every entity needs a key. A key has a subset of fields, implements BK and is immutable. It represents the natural identifier of an entity. In some cases a key may be composed of another key or even multiple. Keep in mind that this may lead to very large keys. Consider synthetic keys, like a UUID in this case.

import de.loosetie.crudular.api.BK

data class SampleKey(
    val name: String,
) : BK<Sample>

Optionally you can specify a Search for your entity. This class is named <Entity name>Search and implements the Search interface. This will enable filtering for your repository. Annotate a property with CrudSearchField to configure conditions.

import de.loosetie.crudular.api.data.Search
import de.loosetie.crudular.api.entity.CrudSearchField

class SampleSearch(
    @CrudSearchField("name", CrudSearchType.STARTS_WITH)
    val nameStartsWith: String? = null,
    override val pageSize: Int,
    override val jitter: Int,
    override val pivotKey: SampleKey?,
    override val orderBy: List<Search.Order>?
) : Search<Sample>

Customize behavior

CRUDular allows the customization of many aspects. This section describes the main entry points for lightweight customization. If you feel like the only thing you do is customizing, please reconsider your use case and the tooling.

Annotations

The simplest approach and also the most limited approach is to use the following annotations:

  • CrudEntity
  • CrudField
  • CrudSearchField

See the kdoc for more.

Implementations

Supply implementations to the following interfaces, to customize CRUDular in depth:

REST

  • CrudHalCreate
  • CrudHalRead
  • CrudHalSearch
  • CrudHalUpdate
  • CrudHalDelete

Service

  • CrudServiceCreate
  • CrudServiceRead
  • CrudServiceSearch
  • CrudServiceUpdate
  • CrudServiceDelete

Repo

  • RepoValueMapper to customize data conversion.
  • CrudRepoAfterCreate,
  • CrudRepoAfterUpdate or
  • CrudRepoBeforeDelete to add behavior at the lowest level.

Writeable views

To make a view entity writeable, first make sure all the non required values have defaults. Then implement CrudServiceCreate and/or CrudServiceUpdate to handle those methods properly.

Extend CRUDular

REST

To support all features make use of the following:

  • de.loosetie.crudular.api.meta.MetaModel
  • de.loosetie.crudular.api.rest.hal.HalDocument
  • de.loosetie.crudular.api.rest.hal.HalLink
  • de.loosetie.crudular.rest.ProblemDetails
  • de.loosetie.crudular.api.service.CrudService

Process:

  1. Parse key:

    part of the URL, de.loosetie.crudular.api.rest.FromUriUtil can help

  2. Parse Payload:

    Keep in mind to only respect enabled fields. Check the MetaModel: type -> entity -> fieldsRestCreate, fieldsRestUpdate

  3. Validate the key/payload:

    Support jakarta.validation.Validator

  4. Call the service

  5. Create HalDocument:

    Add links to self, fields which contain a key and back links: type -> referencedBy See de.loosetie.crudular.api.rest.ToUriUtil

Repo

Implement the following interfaces to support more frameworks, databases or other infrastructure.

  • RepoSpecific
  • RepoBinding
  • RepositoryConfigurer