Crnk: JSON API library
Star
News    Documentation    Related Projects

Crank up the development of RESTful applications with Crnk (pronounced Crank). Crnk is a native resource-oriented rest library where resources and relationships are first class citizens and not just path mappings on methods. This simplifies development and opens up many, powerful new possibilities. Crnk follows the JSON API specification and recommendations in Java to facilitate building RESTful applications. To quote the specification:

By following shared conventions, you can increase productivity, take advantage of generalized tooling, and focus on what matters: your application.

JSON API and Crnk will take care for you of:

27. March 2018: with 2.5.20180326220647 is available and brings support for custom pagination behaviors next to the default offset/limit one. There are some further improvements to the Angular library and Spring setup. For more information see news and documentation.

Features

Easy to integrate with frameworks like Spring, JEE, CDI, JAX-RS, Dropwizard, Servlet API.
Long-term support for technologies such as Java 7 and JPA 2.0 to ease enterprise adoption.
Lightweight with only few dependencies of the core libraries, most notably the Jackson library.
JSON API client and server implementation with the possibility of shared, fully type-safe repository interfaces.
Open Source and released under the Apache License, Version 2.0. license. Suitable for both open source and commercial projects.
Modular design to choose and extend the feature set through a Module API.
Mobile friendly. Fetch complex object graphs in a single request with JSON API inclusions. Fetch partial objects with JSON API sparse field sets. Use together with the popular OkHttp library on Android devices.
Typescript friendly. Generate Typescript interfaces to access your repositories and resources in a type-safe manner.
Expose any JPA entity as JSON API resource with out-of-the-box sorting, filtering, paging and CRUD support without any implementation work. Or further map your entities to DTOs.
Standardized error handling with support for JSR-303 Bean Validation and a variety of other standard Java exception types.
Atomically insert, update and delete multiple resources with JSON Patch.
Integrate into Zipkin to trace incoming and outgoing requests.
Up-to-date documentation with Asciidoc.
Secure your repositories on repository, resource and field level.
Support for Angular development with @crnk/angular-ngrx.
Focus on quality with continuous releases and extensive test coverage.

Coverage Status.

Extensive meta model of your repositories available as JSON API repositories for the purpose of documentation and UI automation.

Example applications

Crnk comes with multiple examples. There is a main example application in a dedicated repository available from crnk-example. It shows an end-to-end example with Crnk, Angular, Spring Boot and ngrx-json-api. Then there are smaller example applications that how to integrate Crnk into various frameworks like Dropwizard, JEE and Spring Boot, see crnk-examples.

The main example application can either be run with docker or by checking it out and run with Gradle:

gradlew run
docker run --name=crnk -p 8080:8080 crnk/example
	
More information and example URLs that show the power of Crnk are available here. A number of URLs to play around (there are more on the example page):

http://127.0.0.1:8080/ Angular frontend application.
http://127.0.0.1:8080/api/ JSON Home document provided by crnk-home listing all repositories.
http://127.0.0.1:8080/api/browse/ crnk-ui allowing to browse all repositories.
http://127.0.0.1:8080/api/votes Simple in-memory repository with an artificial delay (VoteRepositoryImpl.java).
http://127.0.0.1:8080/api/movie MovieEntity JPA entity exposed as repository with crnk-jpa. See ModuleConfig.java.
http://127.0.0.1:8080/api/schedule ScheduleEntity Another JPA entity exposed as repository with crnk-jpa. See ModuleConfig.java.
http://127.0.0.1:8080/api/schedule?filter[name]
[GT]=schedule1&page[limit]=3&sort=-name
Sorting, filtering and pagination. Notice the total resource count in the meta data and the various pagination links.
http://127.0.0.1:8080/api/scheduleDto ScheduleEntity mapped to ScheduleDto and exposed as repository. Notice the additional computed attribute upperName. For the setup checkout ModuleConfig.java.
http://127.0.0.1:8080/meta/resource Meta data of all resources provided by crnk-meta. Have a look also at the relationships, such as to the attributes.

Have a look at the console when issuing requests. crnk-brave with a log reporter is used for tracing.

Setup in 5 Minutes

The following gives a brief example of how to setup Crnk with JAX-RS and CDI. For more information have a look at the documentation and join us on Gitter and GitHub!

Add crnk-core together with the CDI and JAX-RS integrations to your classpath:
compile 'io.crnk:crnk-core:${version}'
compile 'io.crnk:crnk-cdi:${version}'
compile 'io.crnk:crnk-rs:${version}'
					

Setup a JAX-RS application and add the CrnkFeature. In this example a default page size and some meaningful Jackson settings are further configured.

@ApplicationPath("/api")
@Singleton
public class DemoApplication extends Application {

	private static final Long DEFAULT_PAGE_SIZE = 20L;

	@Override
	public Set<Object> getSingletons() {
		CrnkFeature crnk = new CrnkFeature();
		crnk.setDefaultPageLimit(DEFAULT_PAGE_SIZE);

		ObjectMapper objectMapper = crnk.getObjectMapper();
		objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
		objectMapper.findAndRegisterModules();
		objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

		return new HashSet<>(Arrays.asList(crnk));
	}
}
					
The actual data objects known as resource in JSON API terminology looks like:
@JsonApiResource(type = "schedules")
public class Schedule {

	@JsonApiId
	private Long id;

	private String name;

	public Long getId() {
		return id;
	}

	public Schedule setId(Long id) {
		this.id = id;
		return this;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

					
Specifying an interface for your repository is an optional step, but can be quite convient when working on the client-side (like for testing). This particular example further specifies the structure of links and meta data attached to the actual data.
public interface ScheduleRepository extends ResourceRepositoryV2<Schedule, Long> {

	@Override
	ScheduleList findAll(QuerySpec querySpec);

	class ScheduleList extends ResourceListBase<Schedule, ScheduleListMeta, ScheduleListLinks> {

	}

	class ScheduleListLinks extends DefaultPagedLinksInformation implements LinksInformation {

		private String someLink = "http://...";

		public String getSomeLink() {
			return someLink;
		}

		public void setSomeLink(String someLink) {
			this.someLink = someLink;
		}
	}

	class ScheduleListMeta implements MetaInformation {

		private String someMeta = "value";

		public String getSomeMeta() {
			return someMeta;
		}

		public void setSomeMeta(String someMeta) {
			this.someMeta = someMeta;
		}
	}
}
					
The actual implementation of a JSON API repository. This example stores all data in-memory. Notice the QuerySpec.apply to do sorting, filtering and patching in-memory. More advanced use cases will translate the QuerySpec to a native query of the underlying data store. The @ApplicationScoped will lead for the repository to get picked up by the crnk-cdi integration.

@ApplicationScoped
public class ScheduleRepositoryImpl extends ResourceRepositoryBase<Schedule, Long> implements ScheduleRepository {

	private static Map<Long, Schedule> schedules = new HashMap<>();

	public ScheduleRepositoryImpl() {
		super(Schedule.class);
	}

	public static void clear() {
		schedules.clear();
	}

	@Override
	public ScheduleList findAll(QuerySpec querySpec) {
		ScheduleList list = new ScheduleList();
		list.addAll(querySpec.apply(schedules.values()));
		list.setLinks(new ScheduleListLinks());
		list.setMeta(new ScheduleListMeta());
		return list;
	}

	@Override
	public <S extends Schedule> S save(S entity) {
		schedules.put(entity.getId(), entity);
		return null;
	}

	@Override
	public void delete(Long id) {
		schedules.remove(id);
	}
}
					

Roadmap