Fabian Beier-Trampusch Blog

» Knowledge through passion. «

Building a web application with AngularJS / Spring Data Rest and best practices (part 1 of 3)
#AngularJS and Spring Data Rest Series 

Deprecation notice:
This series is deprecated, part two and three will probably not be written anymore. However, this can be used as an introduction to Spring Data Rest.

Welcome to part one of this series about building a web application using AngularJS and Spring Data Rest. You will learn how to:

  • build a Java-based CRUD web application using AngularJS and Spring Data Rest
  • consume the REST API of Spring Data Rest in AngularJS in a reusable way
  • use Spring Data JPA to abstract from the underlying RDBM
  • properly map entities and associations with the help of Spring Data Rest
  • combine the validation mechanisms of Spring Data Rest and AngularJS
  • authenticate your users with Spring Security
  • use the event mechanisms of Spring Data Rest to satisfy complexer business constraints
  • use Gradle, Bower and Gulp for dependency management, building and test running
  • organize your files accurately

This post focuses on setting up the Java-based REST API.

Step 1: The project setup

Alright, lets start with your project setup. I will not make any assumptions about your IDE, but i will assume that it has support for Gradle. So, what is this thing called “Gradle” you might ask?

Gradle

[…] Declare and execute all tasks necessary to compile, test, package and ship multi-language multi-platform multi-project and multi-channel software […].

Introduction quote taken from http://www.gradle.org/

To keep it short: Gradle automates stuff. It is able to automate everything, e.g. building and deploying your project. This can dramatically simplify your workflow! And, aside from that, it helps you with managing your dependencies to third party libraries by downloading them for you. If you have worked previously with build or dependency management tools like Maven or Ant, then Gradle surely sounds familiar.
Later we will integrate the dependency fetching of bower with Gradle, so that you just have to run one command on a clean computer to build and run your application. Trust me, once you are getting used to the comfort of builds with Gradle, you will never want to miss it!

Spring Initializr

Alright, lets start! Open Spring Initializr in a new tab (or window, if you prefer that). The page you just opened helps you setting up a new project by asking you some questions about the libraries you are going to use. On the left side, next to Type, you should select Gradle Project in the drop-down menu. Select Spring Boot Version 1.3.0M4. On the right side, you should check the following dependencies:

  1. Security (Core)
  2. Rest Repositories (Web)
  3. Thymeleaf (Template Engines)
  4. JPA (Data)
  5. H2 (Database)

You may now press Generate Project. Extract the contents of the zip File to a destination of your choice. We are now ready to import the project into our IDE. If you are using eclipse, install the official Gradle Integration. If you are using IntelliJ, you are ready to go.

Step 2: The minimal REST API with Spring Data Rest

We are almost ready to go. We could run the application now, but we would not see very much. Therefore, let’s make two further improvements:

Deactivate Spring Security (for now)

In the current state would our web application not be accessible, because spring security requires us to authenticate. Lets disable authentication until we can manage user accounts. Open /src/main/resources/application.properties and add:

security.basic.enabled=false

Further Information:
There are a lot more configuration options for the application.properties file. All possible configuration keys can be found here:
http://docs.spring.io/spring-boot/docs/1.3.0.M4/reference/htmlsingle/#appendix

Add a note entity and a repository

Every application has some kind of data which is created, read, updated and deleted (CRUD). Classes which hold data are often referred to as entities. Spring Data allows us to create so called repositories for our entities. Repositories provide all of the CRUD methods (creating, reading, updating and deleting an entity) in a reusable manner, so that we do not have to write the boilerplate code for interacting with the database. If you have experience with JPA, than you will be surprised how Spring Data accelerates and simplifies the development even further!

Note class

Copy the following contents into src/main/java/demo/domain/note/Note.java

 1package demo.domain.note;
 2
 3import javax.persistence.Entity;
 4import javax.persistence.GeneratedValue;
 5import javax.persistence.Id;
 6
 7@Entity // Annotation from JPA, declares this as a entity
 8public class Note {
 9
10    @GeneratedValue // Hint to JPA that this field gets generated by the database
11    @Id             // Hint to JPA that this field is the primary key
12    private Integer id;
13    private String title;
14    private String content;
15
16    public Integer getId() {
17        return id;
18    }
19
20    public void setId(Integer id) {
21        this.id = id;
22    }
23
24    public String getTitle() {
25        return title;
26    }
27
28    public void setTitle(String title) {
29        this.title = title;
30    }
31
32    public String getContent() {
33        return content;
34    }
35
36    public void setContent(String content) {
37        this.content = content;
38    }
39}

Repository Interface

Copy the following contents into src/main/java/demo/domain/note/NoteRepository.java

1package demo.domain.note;
2
3import org.springframework.data.repository.CrudRepository;
4import org.springframework.stereotype.Repository;
5
6@Repository
7public interface NoteRepository extends CrudRepository<Note, Integer> {
8}

The highlighted line is part of the magic from Spring Data. The CrudRepository<Note, Integer> provides the aforementioned CRUD operations. Of course are we able to further customize it, but for the moment should you enjoy that you only have to write an interface declaration and let the framework handle the concrete implementation for you. By the way, the generic arguments <Note, Integer> are the entity (in our case a Note) and the type of the primary key for this entity. You may want to have a look at the JavaDoc of the CrudRepository for further information.

Folder and package structure

As you now have created the notes entity and a corresponding repository, let us take a short break and reflect on how we have arranged it.

src/main/java/demo
 ^    ^    ^    ^
 |    |    |    | the main project folder.
 |    |    |
 |    |    | java contains - well - Java source files. You might ask, what else could here be?
 |    |      Groovy for example, an alternative language for the JVM.
 |    |
 |    | main stands in this case for the main source files. On this level you would also have test,
 |      which will contain the unit tests (in a later post).
 |
 | The main source directory. Everything in it will be executable.
   We have a /res directory on this level too. res stands for resources
   and contains other files related to the project, e.g. HTML and JavaScript

Inside the main project folder the structure looks like this
demo (main project folder)
└───domain (contains everything related to our entities and domain objects)
    ├───notes (contains everything related to notes, e.g. the entity and the repository)
    └───categories (not there yet, but would contain anything related to categories)

For futher discussion about the folder/package structure I can recommend this article.

Step 3: running the application

We are now ready to run the application. Double click on the “bootRun” task to compile and start the application.

After some time you should read in the log FrameworkServlet 'dispatcherServlet': initialization completed in 17 ms. You can now open your browser and point it to http://localhost:8080/. If a download dialog appears please install the JsonView-Extension for Firefox or the JsonView-Extension for Chrome.

You should now see output similiar to this:

{
  "_links" : {
    "notes" : {
      "href" : "http://localhost:8080/notes",
      "templated" : false
    },
    "profile" : {
      "href" : "http://localhost:8080/alps",
      "templated" : false
    }
  }
}
[fix display in atom]: <> (__)

ALPS (Application-Level Profile Semantics)

The answer was generated by Spring Data Rest. Lets get in detail with the design of the answer, as it plays an essential role and understanding it is very important. _links is always a pointer to further resources, often including the current displayed resource. profile is a link to ALPS (Application-Level Profile Semantics). This is especially useful for consumers of your REST API, as ALPS describe the semantics of it. It also contains the structure of the REST accessible entities.

Notes REST representation

Lets follow the href of notes to http://localhost:8080/notes.

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/notes",
      "templated" : false
    }
  },
  "_embedded" : {
    "notes" : [ ]
  }
}
[fix display in atom]: <> (__)

Further Information:
Spring has out of the box support for a web user interface for managing a h2 database. You can now add spring.h2.console.enabled=true in a new line to your application.properties. If you restart your application now, you are able to access http://localhost:8080/h2-console/

Log in with the following data:

After a click on connect you have just opened the web console. In it, you can explore the created database scheme and issue queries. Try

SELECT * FROM NOTE
and
INSERT INTO NOTE(TITLE, CONTENT) VALUES ('web console', 'some new content')

For further information have a look at:
https://springframework.guru/using-the-h2-database-console-in-spring-boot-with-spring-security/

The response contains two elements. _links and _embedded. The first one should be familiar now. It contains in this case the link to the current resource - the so called self-link. _embedded contains notes, and notes is an empty array. Maybe you have guessed it! It is an empty array, because we do not have any information stored yet. Lets add some sample data. Create the file src/main/resources/data.sql with the following content.

INSERT INTO note(title, content) VALUES ('testtitle', 'some content here');
INSERT INTO note(title, content) VALUES ('another title', 'some other content here');

Restart the application and access http://localhost:8080/notes again. The following lines are the contents of the previously empty array:

{
  "notes" : [ {
    "title" : "testtitle",
    "content" : "some content here",
    "_links" : {
      "self" : {
        "href" : "http://localhost:8080/notes/1",
        "templated" : false
      },
      "note" : {
        "href" : "http://localhost:8080/notes/1",
        "templated" : false
      }
    }
  }, {
    "title" : "another title",
    "content" : "some other content here",
    "_links" : {
      "self" : {
        "href" : "http://localhost:8080/notes/2",
        "templated" : false
      },
      "note" : {
        "href" : "http://localhost:8080/notes/2",
        "templated" : false
      }
    }
  } ]
}
[fix display in atom]: <> (__)

Woah! It works! But why was data.sql automatically executed? Answer: It is Spring Boot magic.
Let us examine the response further. notes is now an array consisting of two note entities. Each of them has a title-, a content- and a _links-property. title and content are what you expect them to be. _links contains two links on each note entity. Actually, you can ignore the "note"-link, it is the same as the self-link and not mentioned in the Spring Data Rest docs.
The self-link plays, as mentioned before, an important role. If you want to update e.g. the title of the first note entity, you have to issue HTTP-Request to the self link of the note.

Creating, Updating and deleting: PATCH, PUT, POST, DELETE?

You have now a REST API for managing your notes. It will be consumed by your AngularJS application. To create and manipulate your notes, you will need to use the HTTP-Verbs POST, PUT, PATCH and DELETE.

If you want to try the following requests, depending on your browser, you may install an extension for easier crafting of custom requests.

For configuration of the extensions you can use the following screenshots. Please note, that you shall always set the Content-Type-Header to application/json.

POST - create new entities

We have manually created some notes via an SQL script, which is ran on application startup. You probably want to create new notes in your application dynamically. All you have to do is issue a POST-Request to http://localhost:8080/notes with the following content:

{
  "title": "enter your title here",
  "content": "enter your content here"
}

Modifying Entities: PUT/PATCH

If you want to update an existing note, you have two ways for doing so. You either can replace the complete entity with whole new data, or you can partially modify some properties.

PUT - replace existing entities completely

Issuing the following request sets the properties accordingly:

Example: PUT http://localhost:8080/notes/3

{
  "content": "some new content",
  "title": "some new title"
}

Attention: if you forget a property in your request, it will be set to null!

Example: PUT http://localhost:8080/notes/3

{
  "content": "some new content"
}
would lead to the following persisted entity:
{
  "title" : null,
  "content" : "some new content",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/notes/3",
      "templated" : false
    },
    "note" : {
      "href" : "http://localhost:8080/notes/3",
      "templated" : false
    }
  }
}
[fix display in atom]: <> (__)

PATCH - update existing entities partly

As said before, PATCH requests just update the given properties. You do not have to fear, that other properties are modified.

Example: PATCH http://localhost:8080/notes/3

{
  "content": "some new content"
}
would lead to the following persisted entity:
{
  "title" : "the old title",
  "content" : "some new content",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/notes/3",
      "templated" : false
    },
    "note" : {
      "href" : "http://localhost:8080/notes/3",
      "templated" : false
    }
  }
}
[fix display in atom]: <> (__)

DELETE - remove existing entities

Finally you can issue a DELET request to remove the addressed entity. Example: Issue a DELETE to http://localhost:8080/notes/3. Now try to access http://localhost:8080/notes/3. You get a blank screen. Why? Because the server replies with the status code “404 - Not found”.

Summary

You have learned how to create the project from scratch, using Gradle and Spring Initializr. You have created a Note entity and the corresponding repository. You played around with the REST API to get a feeling for how to use it to retrieve, create and manipulate notes.

The next post will focus on setting up the AngularJS part.

Download

You can find the sources for the project in the current state here:

Questions? Comments? Suggestions? I would love to hear from you!