Version: 3.0.20190613135108-BETA

Documentation (pdf)

With this release the first version of Crnk 3.x is released. There have been many incremental improvements to the code base over the past year. In particular the relationship handling has been automated in a number of ways. 3.0 has taken the features and learnings from this time and improves upon some of the configuration defaults. This in turn allows to setup resources and relationships more simply, more quickly and with fewer lines of code. A variety of new features complement the release, most notably the generation of Asciidoc documentation and a type-safe query API. While being a major release, changes required should be minimal (see below).

A first BETA is available. Further BETA releases are going to finalize the new features and providing more documentation and examples.

Cleanup & Migration

Update Examples application

Still work in progress, but crnk-example has been updated to use the various 3.0 features.

Simplified Relationship Setup with @JsonApiRelation.mappedBy

@JsonApiRelation.oppositeName has been deprecated in favor of @JsonApiRelation.mappedBy. It must be set for bi-directional relationships to point from the non-owning to the owning side:
@JsonApiResource(type = "tasks")
public class Task {

    private Project project;


@JsonApiResource(type = "tasks")
public class Task {

    private Set<Task> tasks;


The owning side is typically the single-valued field. This is because the single-valued field is something like a column in a database table that can be accessed and updated. In contrast, the multi-valued field can only be obtained by specifying issuing a query against the single-valued field and as such not suitable to be the owner. The behavior matches with similar JPA-related annotations.

Under the hood the mappedBy property will trigger a suitable default for @JsonApiRelation.repositoryBehavior and @JsonApiRelation.lookup on both sides, simplifying the relationship setup for most use cases. If no relationship repository can be setup, Crnk will no fail directly upon startup.

Improved default values for @JsonApiRelation

Relationship implementation can greatly vary. For this reason the various attributes of @JsonApiRelation historically had most features disabled by default (forwarding, serialization, etc.) to let developers configure what is needed. Over the past year a variety of tooling has been added to simplify the relationship setup, which in turn made the conservative defaults less suited than in the past. With this release new defaults have been introduced that inspect the resources and repositories more closely to decide for a particular default value for @JsonApiRelation.lookUp and @JsonApiRelation.repositoryBehavior.

In many cases it should no longer be necessary to specify those properties, for example:

@JsonApiResource(type = "task")
public class Task {
    public String id;

    @JsonApiRelation // not backed by repository
    public Project project;

    @JsonApiRelation // backed by repository
    public List subTasks;

@JsonApiResource(type = "project")
public class Project {
    public String id;

    @JsonApiRelation(mappedBy = "project")
    public Set tasks;

    public Project parentId;

    public Project parent;

class SubTaskRepository extends OneRelationshipRepositoryBase {
    public RelationshipMatcher getMatcher() {
        RelationshipMatcher matcher = new RelationshipMatcher();
        return matcher;
Have a look at the updated documentation and the new example in the documentation. More information is available in the documentation. Please provide feedback in this area!

Go Faster with Graal Native

This feature will be available in a subsequent BETA release

Sub-100ms startup times and reduced memory requirements to reduce cost and open up new application fields like serverless computing.

Type-safe QuerySpec construction with a Crnk Annotation Processor

Available as a preview in the first BETA

An annotation processor is introduced that allows the type-safe construction of QuerySpec and PathSpec.
UserQuerySpec querySpec = new UserQuerySpec();
        querySpec.filter().projects().id().filter(FilterOperator.EQ, 12);
More information can be found here.

More flexible Gradle generator plugin

The Gradle Typescript generator adds support for the generation of artifacts other than Typescript.
buildscript {
	dependencies {
		classpath "io.crnk:crnk-gen-gradle:${version}"

apply plugin: 'crnk-gen'
crnkGen {
    runtime {
        configuration = 'typescriptGenRuntime'

    forked = true
    resourcePackages = ['io.crnk.example.api']

    typescript {
        enabled = true
        format = 'PLAINJSON'
        genDir = file('src/resources')
For this purpose the project has been split into `crnk-gen-gradle`, `crnk-gen-typescript` and a number of helper projects. More information about Typescript generation is available here.

Documentation generation with AsciiDoc

The new crnk-gen-asciidoc project helps automating REST documentation. It makes use of two kinds of sources: resource classes and test cases. The resource classes give an overview of a REST endpoint with a list of available resources, attributes and relationships. Whereas test cases can serve as examples for developers. This mix of model-driven and test-driven documentation together with the standardization provided by JSON:API can greatly help in writing consistent, documented REST endpoints.

For more information have a look at the documentation and crnk-example.

Allow QuerySpec conversion to Criteria query

The following code snipped shows how to convert a QuerySpec to Criteria query without involving repositories:
JpaCriteriaQueryFactory queryFactory = JpaCriteriaQueryFactory.newInstance(em);

PathSpec idAttr = PathSpec.of(TestEntity.ATTR_id);
QuerySpec querySpec = new QuerySpec(TestEntity.class);
querySpec.addFilter(idAttr.filter(FilterOperator.GT, 0L));

JpaCriteriaQuery<TestEntity> query = queryFactory.query(TestEntity.class);
JpaQueryExecutor<TestEntity> executor = query.buildExecutor(querySpec);
List<TestEntity> resultList = executor.getResultList();

InMemoryHttpAdapter to connect CrnkClient with a server

There is a new InMemoryHttpAdapter implementation completing OkHttp, Apache HTTP client and RestTemplate. InMemoryHttpAdapter takes a CrnkBoot instance as parameter to directly connecting to a server without having to go through any TCP/HTTP transport layer. Requests are still serialized and deserialized, but no running HTTP server is necessary. Unit testing can greatly benefit from such a more simpler and faster setup:
    String url = ...
    CrnkClient client = new CnrkClient(url).
    client.setHttpAdapter(new InMemoryHttpAdapter(crnkBoot, url));

DataRoom Filtering

Available with Beta 2.

The SecurityModule of crnk-security has been enhanced to allow dataroom access control. A DataRoomFilter can specify what a caller is allowed to see in the form of a QuerySpec. This QuerySpec is added to any repository call complementing the parameters provided by the caller. In the example here all callers are just allowed to see tasks with name "foo":
Builder builder = SecurityConfig.builder();
builder.setDataRoomFilter((querySpec, method) -> {
    if (querySpec.getResourceClass() == Task.class) {
        QuerySpec clone = querySpec.clone();
        clone.addFilter(PathSpec.of("name").filter(FilterOperator.EQ, "foo"));
        return clone;
    return querySpec;
SecurityConfig config =;
securityModule = SecurityModule.newServerModule(config);
Real-world example would here access the user principal/roles to make up a decision. The provided QuerySpec not checks access for GET, but also PATCH, POST and DELETE by fetching the existing entry and verify access. More information is available in the documentation.

HttpAdapterListener for a vendor-neutral HTTP request/response interception

A new HttpAdapterListener can be added with addListener to HttpAdapter. It allows to cover many of the typical use cases like adding security headers without having to get and customize the underlying HTTP implementation.

crnk-format-plain-json in CrnkClient

The PlainJsonFormatModule can now be added to CrnkClient. This is in particular useful together with the Asciidoc generator to create documentation in the plain json format for consumer making use of it.

Prettier URLs computation

Computed links (CrnkClient, pagination, etc.) by default omit the `EQ` operator and type information where not necessary. This leads to more readable URLs like:[name]=Doe
rather than[tasks][name][EQ]=Doe

OpenTracing Support

OpenTracing is a vendor-neutral API to write trace information. crnk-monitor-opentracing introduces a new module OpenTracingServerModule that brings support for OpenTracing to Crnk. It can be used properly set the names of spans. More information is available in the documentation.

Singular Nested Resources

Support for nested resources has been improved by introducing support singular nested resources. Thereby a parent can hold a single nested resource as single-valued relationships. Urls then look like:

An example looks like:

@JsonApiResource(type = "header", nested = true)
public class PostHeader {

	private String postId;

	private String value;

	@JsonApiRelation(opposite = "header", lookUp = LookupIncludeBehavior.AUTOMATICALLY_WHEN_NULL,
			repositoryBehavior = RelationshipRepositoryBehavior.FORWARD_OWNER)
	private Post post;

More information can be found here.

Specify access mode with @JsonApiResource

Available in Beta 2

@JsonApiResource has new properties to configure the allowed access modes to a resource: sortable, filterable, postable, readable, patchable and deletable. An example looks like:
    type = "tasks", sortable = false, filterable = true,
    postable = false, readable = false, patchable = false, deletable = false
public class AccessDeniedTestResource {


The new properties match the ones from @JsonApiField and provide the base line for it. For example, if a resource does not allow filtering, none of its fields will.

Enum support for Activiti

crnk-data-jpa maps Enum types to Strings for Activiti. This to by-pass the regular Activiti serialization mechanism that treats the enum like a java.lang.Object by storing it is as serialized blob and not supporting filtering.

More flexible EntityManager setup with JPA module

JpaModule.createServerModule(...) comes with a new flavor that allows to pass the EntityManager as java.util.Supplier. Every time a repository makes use of the EntityManager, the supplier is called and must return an instance. The supplier together with the existing TransactionRunner allows for custom JPA setups and transaction handling.

New Security-related repositories

the SecurityModule sets up repositories to make its internal rules available from the REST API:

Support for GROUP operator by facets

The FacetModule gained support for the GROUP operator:[values][GROUP]=priority,name
It works equivalent to SQL GROUP BY queries. In the example a facet count is computed for every pair of priority and name attribute.

Support for JsonInclude.Include.NON_EMPTY

For example in the subsequent snippet the default 0 value will be omitted:
private long id;