Boot Your RESTful API Using Spring

With microservices being a popular approach on greenfield projects and equally used the liven up aging architectures, I wanted to explore what the Groovy / Spring world had to offer in this regard. Spring Boot is arguably the simplest way to get a lightweight RESTful service off the ground. In this post I will cover the basics on getting a HATEOAS RESTful service that stores Person entities in a Mongo database.

The code can be found on github.

What is HAL & HATEOAS?

HATEOAS (Hypermedia as the Engine of Application State) is a style of REST application architecture that allows a client to dynamically navigate the related resources through hypermedia links in the body of a response. HAL (Hypertext Application Language) defines a simple format to hyperlink between resources in your API, thus HAL can be used to implement HATEOAS.

Environment

Having stared at Maven xml files for many years I felt inclined to see how the other side lives and decided to take Gradle for a test drive. It did not take a lot of effort to get the build going but you do need to have a few obvious components installed before you can start.

Java

Make sure java is installed and on your path. I like to install it in /opt with a symlink current pointing to the version. Then stick current into your profile, this allows you to easily switch between versions by just changing the current symlink.

mkdir /opt/java && cd /opt/java

curl -v -j -k -L -H "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u25-b17/jdk-8u25-linux-x64.tar.gz > jdk-8u25-linux-x64.tar.gz

tar -xvf jdk-8u25-linux-x64.tar.gz
ln -sf /opt/java/jdk1.8.0_71 /opt/java/current
echo 'export JAVA_HOME=/opt/java/current' >> ~/.profile
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.profile
source ~/.profile

SDKMAN!

Nope, its not a superhero, but can easily pass for one. You can think of it as RVM for Java or Groovy.

curl -s get.sdkman.io | bash
source "/home/vagrant/.sdkman/bin/sdkman-init.sh"
sdk install springboot

Now you should have the spring command available in your terminal.

Mongo

Mongo is a popular document store that will be used to persist the Person entities.

mkdir /opt/mongo
cd /opt/mongo
wget wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1404-3.2.1.tgz
tar -xvf mongodb-linux-x86_64-ubuntu1404-3.2.1.tgz
ln -sf mongodb-linux-x86_64-ubuntu1404-3.2.1 current
mkdir /opt/mongo/data
/opt/mongo/current/bin/mongod --dbpath /opt/mongo/data/

Bootstrapping the project

I always find it easier to tinker with something that is working when you are learning a new technology. Spring initializr provides a browser based wizard to generate a project skeleton. Make sure you select Gradle project with the following starters:

  • Rest Repositories
  • MongoDB
  • Actuator

Extract the generated zip archive in your project folder. The project's root folder will contain a set of Gradle wrapper scripts and jar file that provides a convenient way of bootstrapping your gradle build environment. It can be committed with the rest of the project files and will download and cache the version of Gradle used for the project.

To build the project run:

./gradlew

This might take a while the first time since it needs to download Gradle and all the projects dependencies but stay calm and don't panic, the second time will be much faster since all the dependencies are cached locally.

Hotswapping with Spring-Loaded

This might speed up your development cycle to a certain extent. It will reload classes that have changed without restarting the app. Unfortunately intellij does not recompile automatically, when a source file changes you have to force it to compile (SHIFT + COMMAND + F9 on Mac). Eclipse users don't have this problem.

Download spring-loaded then start the main app with javaagent:

-javaagent:$PATH_TO_JAR/springloaded-1.2.5.RELEASE.jar -noverify

The Code

This example comes straight from Spring.

The first step is to create an Entity that you would like to expose as a RESTful service.

package codes.monkey.people

import org.springframework.data.annotation.Id

class Person {
    @Id
    String id

    String firstName, lastName
}

and now for the best part, create Repository Interface and Spring will take care of the rest! No implementation required.

package codes.monkey.people
//...

@RepositoryRestResource(collectionResourceRel = 'people', path = 'people')
interface PersonRepository extends MongoRepository<Person, String>{

    List<Person> findByLastName(@Param('name') String name)
}

and that is it folks. You now have a HATEOAS RESTful API.

The server can be started through gradlew

 ./gradlew bootRun

To test it you can issue a curl command to create a Person.

curl -i -X POST -H "Content-Type:application/json" -d '{  "firstName" : "Bilbo",  "lastName" : "Baggins" }' http://localhost:8080/people

The RESTful resources can be naturally discovered in a true HATEOAS fashion:

curl http://localhost:8080

results in

{
  "_links" : {
    "people" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:8080/profile"
    }
  }
}

Custom search queries like findByLastName will be available under /people/search/findByLastName{?name}.

Tweaks

Spring Boot supports configuration changes through properties or yaml files which can be activated through Spring Profiles. For example, to change the host that MongoDB run's on, you can edit application.properties or application-$profile.properties and add the following entry:

spring.data.mongodb.host=someotherhost

The Spring Boot documentation contains a list of supported configuration keys.

Packaging

The Gradle build contains a task to build a single executable jar that contains all of the required dependencies to run the app.

./gradlew build
java -jar build/libs/spring-boot-restful-0.0.1-SNAPSHOT.jar

Conclusion

Most of this post was taken up by getting the environment configured, that aside, with Spring Boot you really do get a lot in return for your code. As this example shows, a full blown HATEOAS RESTful API with MongoDB backed storage for only a few lines of code by Java standards.

References

Accessing MongoDB Data with REST