JSON:API with Crnk
Crank up the development
of resource-oriented APIs.
Learn more in the examples and documentation.


info

14. July 2019: With 3.0.20190714142556 there is new major release available! There have been many incremental improvements to the code base over the past year. 3.0 has taken the features and learnings from this time and improves upon some of the configuration defaults to make better use of the newer features. This in turn allows, for exampple, to setup resources and relationships more simply, more quickly and with fewer lines of code. A variety of new features complement this release.

Crnk is a native resource-oriented rest library where resources and relationships are first class citizens and not just path mappings on methods. Model your application as resources and relationships and implement the repositories to access them. This simplifies development and opens up many, powerful new possibilities:
  • Fully transparent, automated HATEOAS setup.
  • Fetch complex object graphs with the inclusion of relationships to perform well even with mobile connections.
  • Let your API evolve with the introduction of new attributes and relationship and have it discoverable. Stop handing out RPC-style APIs where every further added, isolated service makes the system less understandable.
All batteries included: Crnk will take care of everything else from low-level REST protocol details to resource linking and error handling. Default implementations will let you:
  • expose relational databases with JPA.
  • validate incoming data with JSR-303.
  • browse in-memory data structures with sorting, filtering and paging like any other data source.
  • get an overview of your data with built-in facetted search.
Application as depicted in the screenshot are implemented with virtually no backend code.
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.

Applications gain a rich, discoverable REST API with sorting, filtering, paging, object graphs, HATEOAS and more.
A module mechanism allows to dynamically add interceptors, repositories, filters, security providers and more and provides the basics for many different extensions. This is used, for example, to integrate with OpenTracing as depicted in the screenshot.
Crnk takes an implementation and example-driven approach where curl or a simple browser is sufficient to navigate the API. Complex modelling as in SOAP, gRPC, GraphQL and Swagger is not necessary to avoid any friction area between modelling and implementation for developers.

Meta and presentation models allow to learn about the system at compile and runtime. For example, the Crnk generator can create derived artifacts like Typescript stubs and Asciidoc documentation. The later presents the resource model and showcases verified API calls extracted from running test cases. A cluttering of documentation-related annotations is not necessary.

security
The resource-oriented nature allows for authorization mechanisms that go well beyond traditional REST endpoints:
  • Limit access to resource types and fields. Crnk will take care of protecting everything from the basic endpoints to their relationships and request parameters.
  • Add dataroom protection to limit the number of resources a user can see.
games
Crnk is lightweight, modular and almost dependency-free. It is a library that integrates well with basically everything from Spring Boot, Vert.x, JAX-RS, servlets and others. Its footprint allows to scale from traditional services to serverless applications. And an exchangeable protocol layer allows to move beyond REST where necessary.




Get Started in 5 Minutes

The following gives a brief example of how to setup Crnk with Spring Boot For more information have a look at the examples and documentation and join us on Gitter and GitHub!

Add jcenter as a repository, for example:
repositories {
    jcenter()
}
                    
Add the crnk-bom to your dependency management, for example:
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
    imports {
        mavenBom 'org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE'
        mavenBom "io.crnk:crnk-bom:${CRNK_VERSION}"
    }
}
                    
And add the following dependencies to your classpath:
compile 'io.crnk:crnk-setup-spring-boot2'
compile 'io.crnk:crnk-format-plain-json'
compile 'io.crnk:crnk-home'
compile 'org.springframework.boot:spring-boot-starter-web'
					

Setup a Spring Boot application:

@Configuration
@SpringBootApplication
public class MinimalSpringBootApplication {

	public static void main(String[] args) {
		SpringApplication.run(MinimalSpringBootApplication.class, args);
		System.out.println("visit http://127.0.0.1:8080/ in your browser");
	}
}
					
The actual data objects known as resource in JSON:API terminology looks like:
@JsonApiResource(type = "projects")
public class Project {

	@JsonApiId
	private Long id;

	@JsonProperty
	private String name;

	public Project() {
	}

	public Project(Long id, String name) {
		this.id = id;
		this.name = name;
	}

	public Long getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

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


					
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.

@Component
public class ProjectRepositoryImpl extends ResourceRepositoryBase<Project, Long> {

	private static final AtomicLong ID_GENERATOR = new AtomicLong(124);

	private Map<Long, Project> projects = new HashMap<>();

	public ProjectRepositoryImpl() {
		super(Project.class);
	}

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

	@Override
	public synchronized <S extends Project> S save(S project) {
		if (project.getId() == null) {
			project.setId(ID_GENERATOR.getAndIncrement());
		}
		projects.put(project.getId(), project);
		return project;
	}

	@Override
	public synchronized ResourceList<Project> findAll(QuerySpec querySpec) {
		return querySpec.apply(projects.values());
	}
}