Summary: Tutorial showing how to rapidly develop a REST api in Java using Spring Data Rest, with Javascript frontend using Knockout.js. Complete with sample application (CORS enabled) and code available on Github1.
- Spring notes
- Build tool: Springboot
- Setup
- How to create a REST api in Java
- The domain model
- The Frontend
- Exercise
- Thanks for reading
- Resources
Time again to get our hands dirty. In this post I demonstrate how to create a REST based web application in Java with minimal effort. I’m going to use following frameworks:
I’m going to develop a complete application, with separate backend and frontend. As an example I chose to create a simple bookmark service. As I’m using Intellij Idea and Mac, the examples are geared towards those but it shouldn’t be too difficult to adjust to any other IDE like Eclipse.
Spring notes
If you are new to Spring, the Spring website can be a bit overwhelming in the beginning as there are dozens of projects to choose from. There are basically two ways to learn about Spring: reading a project documentation (which I always recommend to do first before going anywhere else) or starting with a guide.
-
http://spring.io/docs is the starting point for getting to the in-depth documentation about a Spring project. From there you can get to the projects main site (including “quick start” installation guide) or jump directly to the reference documentation or javadocs.
-
http://spring.io/guides collects short intro guides about a wide range of subjects. Start here if you want to quickly try out a feature, I find the guide quite helpful for getting started quickly.
If you haven’t done anything yet in Spring I’d recommend to have a look at the main documentation first: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/
Build tool: Springboot
Docs: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#cli-init
Nowadays, every stack worth it’s salt needs a proper build tool to get up to speed quickly. I’m using Spring boot for that.
Installation: installed on my Mac the Spring Boot CLI via brew
.
With Spring Boot, we can create the project and already specify what modules we are going to use. To get an overview of what is available run spring init --list
.
Setup
Execute:
$ spring init --build=gradle --java-version=1.8 --dependencies=data-rest,data-jpa spring-data-rest-knockout-bookmarks
Edit gradle.build
Change into the new directory. Let’s edit the Gradle build file:
$ edit gradle.build
Remove eclipse plugin and method.
As we use Intellij we can remove the eclipse plugin and method.
Add H2 database
We are going to need some means to persist our data. For demonstration purposes the H2 in memory database is fine.
In dependencies add compile("com.h2database:h2")
Note: if you want to persist the data, you can use H2 in file based mode. I have already prepared the Github project, just remove the comments in src/main/resources/application.properties
and data gets persisted between sessions.
Optional: enable debugging in the Idea
While developing I prefer to be able to debug in Idea. Add following lines to the build file to enable:
applicationDefaultJvmArgs = [
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
]
Optional: enable hot code swapping
Docs: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html#howto-reload-springloaded-gradle-and-intellij
Another small tweak that can come in handy is hot code swapping. The springloaded
module can do that for us.
Add dependency it in buildscript
:
buildscript {
dependencies {
....
classpath("org.springframework:springloaded:${springBootVersion}")
}
}
idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
}
}
However, keep in mind that Intellij does not automatically re-compile classes on saving. In order to make hot code swapping possible, either manually compile classes (eg ctrl+shit+F9
or make project) or enable “Make project automatically” in IntelliJ settings. In the beginning of a project, I prefer the latter. It has no difference on performance (on my 2013 Mac at least).
The finished gradle.build:
Create idea project
Finally, lets create the Idea files. Run:
$ gradle idea
Open in idea.
One last clean up step is to remove the automatically generated demo files (DemoApplication.java
and DemoApplicationTest.java
). Also, Idea might complain about Spring not being configured, you can add the Spring facet to the project (and later add context files, see below).
We are done with setting up the project. Let’s begin to create the backend!
How to create a REST api in Java
The Spring Data Rest module takes care of a lot of boilerplate code without becoming too heavy weight or getting in the way.
Next, I’m basically doing similar steps as in the official quick start guide: http://spring.io/guides/gs/accessing-data-rest/, have a look there as well.
Let’s start with creating first the Spring context.
Add application Spring context
The modern way of Spring configuration is Java-based via annotations (even though in some cases it makes sense to mix old-style xml configuration with annotations). Create following class:
The important detail here are the annotations of our Application
class. It tells Spring to auto-configure our project as a REST application.
The domain model
As already mentioned, Spring Data Rest spares us from developing lots of boilerplate code. All we need to do is to focus on our domain model and Spring will take care of making it available via REST, including HATEOAS compatible communication (more about that later). So next we add the domain model.
Bookmark model
For demonstration purposes I’m going to develop a simple Bookmark application that has only one model class, called (guess what) Bookmark
. And gosh is it simple, it has only one field called url
(well ok it has two fields, the id
is a mandatory identifier):
Not too complicated, is it? Next we need to add a Repository
to tell Spring that we want to expose it as a REST resource. Create following interface along the model:
That was even simpler.
And now what? That’s it! We are done and have a fully fledged REST api at our hands. It can’t get simpler than that.
Run and test
Let’s try it out. Run:
$ gradle run
and send some requests to our backend. I’m using the HTTPie2 client to send requests instead of CURL but use whatever you like.
Send a request to the root url (often called “discovery”):
$ http :8080/
HTTP/1.1 200 OK
Content-Type: application/hal+json;charset=UTF-8
Date: Fri, 10 Oct 2014 13:29:59 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"_links": {
"bookmarks": {
"href": "http://localhost:8080/bookmarks"
},
"profile": {
"href": "http://localhost:8080/alps"
}
}
}
Voilà, the answer is a complete REST response. The Content-Type tells us another interesting fact:
Content-Type: application/hal+json;charset=UTF-8
Spring Data Rest automatically exposes our resources in HATEOAS manner. Specifically, it is using the HAL3 format. (I give more background about HATEOAS here).
So let’s see the bookmarks then, lets do a GET on the bookmarks resource (I’m omitting the headers in the response for now):
$ http :8080/bookmarks
HTTP/1.1 200 OK
{}
Not much going on yet. Let’s create a bookmark:
$ http POST :8080/bookmarks url=news.ycombinator.com
HTTP/1.1 201 Created
Content-Length: 0
Date: Fri, 10 Oct 2014 13:57:20 GMT
Location: http://localhost:8080/bookmarks/1
Server: Apache-Coyote/1.1
Note the Location
header:
Location: http://localhost:8080/bookmarks/1
Looks good. Let’s check:
$ http :8080/bookmarks
{
"_embedded": {
"bookmarks": [
{
"_links": {
"self": {
"href": "http://localhost:8080/bookmarks/1"
}
},
"url": "news.ycombinator.com"
}
]
}
}
$ http :8080/bookmarks/1
{
"_links": {
"self": {
"href": "http://localhost:8080/bookmarks/1"
}
},
"url": "news.ycombinator.com"
}
It works. If you feel like it, try PUT, PATCH, DELETE, HEAD and so on.
Let’s try this…
Ok, so now lets make the bookmarks a tiny bit more interesting. We add a Note
property, which we can use to add notes to a bookmark and that we want to make searchable, and a created
property that should be automatically set when we create a new bookmark.
Extend the domain model:
Set created
field before saving
To add behaviour to the default CRUD resource handling, all we have to do is to add a Handler that registers to different events. In the case of the created
field we want to set it before creating a new resource (and only then). As usual in Spring, this is done via annotations (read more about events here: http://docs.spring.io/spring-data/rest/docs/2.2.1.RELEASE/reference/html/#events-chapter)
Add following file:
In the method with @HandleBeforeCreate
annotation we set the created date of the resource before it gets saved.
All left to do is to tell our main Application configuration about this handler. Edit Application.java
:
Restart and try (adding also a note this time):
$ http POST :8080/bookmarks url=news.ycombinator.com note="this is hacker news"
HTTP/1.1 201 Created
Location: http://localhost:8080/bookmarks/1
$ http :8080/bookmarks/1
{
"_links": {
"self": {
"href": "http://localhost:8080/bookmarks/1"
}
},
"created": "2014-10-10T14:18:19.401+0000",
"note": "this is hacker news",
"url": "news.ycombinator.com"
}
Works!
Search for notes
I haven’t explained yet how the resource get exposed and why it is working out of the box. It is all happening in BookmarkRespository
, by, as you probably have guessed already, extending the CrudRepository
interface. If you look into that interface, you will find typical CRUD operations like save, delete, exists, findAll. Those get mapped to POST, DELETE, HEAD, GET and so on.
Knowing that, we can further extend the Repository Interface with custom queries. Spring Data Rest is a sub project of Spring Data JPA. You can find more info about how to create queries here: http://docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/html/#repositories. All queries that are added to the Repository are made available under the same name (unless otherwise configured).
Let’s extend the repository with a method that searches for the content of a note in an SQL LIKE manner:
We want to expose it under /note
endpoint with text
as the search text.
Let’s have a look at the bookmarks resource:
$ http :8080/bookmarks
{
"_links": {
"search": {
"href": "http://localhost:8080/bookmarks/search"
}
}
}
There it is, a new “search” resource for bookmarks:
$ http :8080/bookmarks/search
{
"_links": {
"findByNoteContaining": {
"href": "http://localhost:8080/bookmarks/search/note{?text}",
"templated": true
}
}
}
I’d say that’s all pretty easy to grasp.
Let’s try it out:
$ http POST :8080/bookmarks url=news.ycombinator.com note="this is hacker news"
HTTP/1.1 201 Created
$ http :8080/bookmarks/search/note text=="hacker"
{
"_embedded": {
"bookmarks": [
{
"_links": {
"self": {
"href": "http://localhost:8080/bookmarks/1"
}
},
"created": "2014-10-10T14:58:13.133+0000",
"note": "this is hacker news",
"url": "news.ycombinator.com"
}
]
}
}
(Note the double equals sign ==
of HTTPie request, indicating that this is a path parameter and not a field of a JSON payload). It works. We don’t need anything else for our tiny bookmark service.
The Frontend
As we went ahead so fast I thought I add a frontend as well to make this a complete web application. I’m using Knockout.js (which fullfils my requirement to be light-weight and unobtrusive), jQuery and a bit of Bootstrap.
If you checkout the Github repo, you’ll see that the whole frontend is made out of two files: index.html
and bookmark.js
.
The Knockout Model and ViewModel defined in bookmark.js
:
It’s out of scope to explain Knockout here (have a look at their interactive tutorial, it’s quite good). The things to note besides the standard Knockout data-bindings (which are used in index.html
, see next) are the jQuery calls to our backend. We are using the bookmarks resource and if you look at the Javascript Bookmark model you notice that we store the self ref of a resource:
With that we can comfortably access the ressource (bookmark.selfHref
), e.g. in delete:
In index.html
we add a form and table and include the Knockout bindings:
The resulting UI looks like this:
The app lets you do all that is necessary: create, edit, delete and open bookmarks.
Now, serve up the file from IntelliJ Idea (open index.html
and select chrome for example). Fill in an url and a note and hit “Add” …
Nothing happens? Doesn’t work?
A look into the Javascript console of Chrome gives us a hint:
Yes indeed, we are building an actually separated client-server web application and have forgotten about this thing called CORS4. It is also out of scope to say more about it (sorry, but maybe in another post), but you can read up on it here: http://en.wikipedia.org/wiki/Cross-origin_resource_sharing. In short, our server needs to allow cross-domain requests and I wanted to show how we can do that.
The last piece: enable CORS on the backend
CORS requires us to respond with certain headers. During our prototyping phase we are going to allow all. This can be done easily with adding a servlet Filter
to our backend.
Add following file:
And similar to the event handler, we modify Application.java
to configure Spring to use the filter. Add following method:
Restart and the app will work. Happy bookmarking :-)
Checkout the source code on Github.
Exercise
If you feel like playing with the tutorial app: can you extend it and add a search field for notes to the UI? And then make url searchable, and after that you could add pagination and ordering (hint: you use a different repository to inherit from)…
In a follow-up post, I have extended the bookmark application with a scraping and source code extraction feature (implemented as a microservice).
Thanks for reading
I hope this post was helpful for you. If you have comments, questions or found a bug please let me know, either in the comments below or contact me directly.