Category Archives for REST

Introduction to Spring Data REST

This tutorial on Spring Data REST shows how Spring Data repositories can be exposed as a REST API. Its a really interesting idea, and can save you a lot of boilerplate code building microservices.

Introduction to Spring Data REST

Spring Boot makes it easy to create a Spring Data REST starter project using Spring Initializr

spring initializr

Spring Initializr – Spring Boot JPA Microservice

Im using Spring Boot 2.0.0, and the HAL Browser to quickly demonstrate REST API.

Download, expand and import into your IDE –

Expanded Spring Boot Project

Expanded Spring Boot Project

Code

For a quickstart the code is available on github at – https://github.com/farrelmr/introtospringdatarest/tree/1.0.0

Let’s keep this simple and use the classes from my parkrunpb project.

The starting point is the pom.xml –

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.javabullets.springdata</groupId>
    <artifactId>jparest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>jparest</name>
    <description>Spring Data Rest Example</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-rest-hal-browser</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

So we have our JPA object –

package com.javabullets.springdata.jparest;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class ParkrunCourse {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;

private String courseName;
private String url;
private Long averageTime;

public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Long getAverageTime() {
return averageTime;
}
public void setAverageTime(Long averageTime) {
this.averageTime = averageTime;
}
}

Im using JPARepository –

package com.javabullets.springdata.jparest;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

public interface ParkrunCourseRepository extends JpaRepository<ParkrunCourse, Long> {
}

And a script to ensure the data is preloaded – stored in (src\main\resources\import.sql) –

INSERT INTO PARKRUN_COURSE(ID, COURSE_NAME, URL, AVERAGE_TIME) VALUES (1, 'Inverness', 'http://www.parkrun.org.uk/inverness/', 1582);
INSERT INTO PARKRUN_COURSE(ID, COURSE_NAME, URL, AVERAGE_TIME) VALUES (2, 'Aberdeen',    'http://www.parkrun.org.uk/aberdeen/', 1586);
INSERT INTO PARKRUN_COURSE(ID, COURSE_NAME, URL, AVERAGE_TIME) VALUES (3, 'Dundee(Camperdown)', 'http://www.parkrun.org.uk/camperdown/', 1752);
INSERT INTO PARKRUN_COURSE(ID, COURSE_NAME, URL, AVERAGE_TIME) VALUES (4, 'St Andrews', 'http://www.parkrun.org.uk/standrews/', 1669);
INSERT INTO PARKRUN_COURSE(ID, COURSE_NAME, URL, AVERAGE_TIME) VALUES (5, 'Perth', 'http://www.parkrun.org.uk/perth/', 1620);
INSERT INTO PARKRUN_COURSE(ID, COURSE_NAME, URL, AVERAGE_TIME) VALUES (6, 'Edinburgh', 'http://www.parkrun.org.uk/edinburgh/', 1523);

Running the Code

Run the code using the maven wrapper – mvnw –

mvnw spring-boot:run

We can then access the HALBrowser on –

http://localhost:8080/browser/index.html#http://localhost:8080/parkrunCourses

Spring Data REST - HALBrowser

Spring Data REST – HALBrowser

The response body returns all the parkrunCourse. You can also do GET, POST, PUT, PATCH and DELETE.

A good trick is to use the browser integrated SQL editor for the H2 database. I covered this in my spring boot security tutorial.

hal+json media type

Spring Data JPA allows you to make calls as json or hal+json. The purpose of hal+json makes it easier to navigate API’s following links. The HALBrowser is great for quickly checking your API.

Conclusions

This example shows how a spring data repository can easily be exposed as a REST API. My next post will look at the practicality of this approach, and what restrictions you might want to apply to your API.

RESTful And HATEOAS Web Services

RESTful – Representational State Transfer

RESTful Web services are an alternative to SOAP. It is designed to be simple, lightweight and fast, with the following constraint’s –

  • Resources – Easily understood directory structure URI
  • Uniform Interface – GET, POST, PUT and DELETE
  • Messages – Can return any format – HTML, XML, plain text, …
  • Stateless – Interactions not stored

Common Operations

Consider the example in my apache cxf jax rs post. The GET, PUT and PATCH operations are idempontent. This means that the same parameters should return the same results

GET – Select – Idempotent

[sourcecode]GET /customerservice/customers/1[/sourcecode]

POST – Create

[sourcecode]POST /customerservice/customers[/sourcecode]

PUT – Update or Create – Idempotent

[sourcecode]PUT /customerservice/customers[/sourcecode]

PATCH – Update only – Idempotent

[sourcecode]PATCH /mypath/1[/sourcecode]

DELETE – Remove record

[sourcecode]DELETE /customerservice/customers/1[/sourcecode]

HATEOAS – Hypermedia as the Engine of Application State

HATEOAS is a further constraint on RESTful services. It basically requires a response to return the location of the data. For example –

[sourcecode] GET customerservice/customers/1 HTTP/1.1 HTTP/1.1 200 OK

<?xml version="1.0"?>
<customer>
<id>1</id>
</customer>
[/sourcecode]

Would become –

[sourcecode lang=”xml”] <?xml version="1.0"?>
<customer>
<id>1</id>
<link rel="self" href="/customerservice/customers/1" />
</customer>
[/sourcecode]

References

http://en.wikipedia.org/wiki/Representational_state_transfer
http://en.wikipedia.org/wiki/HATEOAS
http://docs.oracle.com/javaee/6/tutorial/doc/gijqy.html
https://www.javabullets.com/2015/01/15/apache-cxf-jax-rs/
https://github.com/spring-projects/spring-hateoas