Angular/REST/Java microservice architecture – step-by-step

Introduction

Matthias & Jakob have just joined the team at Armstrong Consulting. As part of the learning process, they get to design & develop a complete application from start to finish using the technology stack they’ll be using on customer projects. In this post, they describe their work.


To familiarize ourselves with all the technologies we will be using day-to-day for customers, we wanted to create an application using as many relevant patterns as possible as an exercise. The application should be based around a stateless microservice REST API, encapsulating application logic (user signon etc.) and other external REST APIs (TMDb, Plex). In this post we are going to outline the steps to develop that application.

Our plan was to create a web application that allows us to conveniently browse movies we get from the TMDb API while also adding our own features (such as synchronisation with a user’s Plex library) on top of that by passing all the requests through our own API.

You can visit the finished application here.

Technology stack:

  • Java
  • Spring Boot (server framework)
  • Typescript, HTML, CSS
  • Angular (web application)
  • Docker, Docker Compose (containerization, deployment)
  • HAProxy (routing, load balancing and fault tolerance)
  • Oracle

Implementing a REST API with Spring Boot

Planning to create a full functioning REST API in Java can be intimidating, but tools like Spring Boot help to make it easier. Spring Boot provides a way to publish RESTful APIs with Spring MVC, and it also allows us to conveniently access a database of our choice with Spring Data and Hibernate as well as providing a security framework with Spring Security to authenticate the users accessing our API.

Step 1: Getting the tools

In order to start a project with Spring Boot we make use of the Spring Initializr. It provides an easy to use UI to configure and generate the project according to our needs with all the necessary dependencies. We used a Maven Project with the latest version of Spring and include the Spring Web Starter dependency. Additionally, we are going to use Eclipse IDE (but feel free to use your preferred IDE as we are not relying on any exclusive features of Eclipse).

Project Setup using Spring Initializr

Step 2: Creating a REST Controller to handle API requests

Before creating the controller, let’s see what we have so far: Since we already have Spring Web Starter in our dependencies, Spring will automatically start a tomcat server on the default port 8080 once we launch the application. If you want to change the port, just go into your src/main/resources/application.properties file and change the port to whatever you need.

You can easily start the server using the IDE of your choice or from the command line in your project directory with the command
$ mvn spring-boot:run

Now you can already browse to http://localhost:8080 and are going be greeted by an error page:

Default 404 Page

Since we don’t have a controller to handle our request yet, Spring responds with its default error controller. So let’s go ahead and create a controller:
All we have to do is to create a new class and annotate it with Spring’s built in annotation @RestController.

Now we can declare as many methods on the controller as we like, and those we annotate with @GetMapping(value=”/api-path”) will automatically be called once a GET request to the specified path is made. Similarly we would use @PostMapping for POST requests and so on…

As you can see in the example above, we declare a method with a return type of type String. Once our method returns, Spring automatically sets a Content-Type header of text/plain, sets a http status code of 200 and off goes the response. Spring does all of this for us, but we can manipulate the request as well as the response directly if we choose so. Just tell spring to give us the request and response objects by declaring them as parameters of our method.

This comes in handy, especially when you want to return a specific status code other then 200, or want to access the request headers for example. Luckily, we are not limited to returning Strings from our methods and we can choose to return objects as well. Spring will automatically convert the object to JSON and set the appropriate Content-Type header.

As you would expect, the response from this method is {"message":"pong"}. One of the last important features we frequently use throughout our controllers, is the convenient handling of path variables and request parameters. Spring provides two annotations @PathVariable and @RequestParam(value=”name”, defaultValue=”default”) for that purpose. Simply give the method a parameter, annotate it accordingly, and it will receive the value, once a request is made.

This approach of creating our API is known as “API-Second” approach, which means that we are implementing the methods first before assigning them to a specific path of our API. This may not be the best way to go for designing APIs but as our whole project and its requirements evolved in some way, it was the way that suited our immediate needs best.

Step 3: Create a User Database Model

Now that we are able to handle requests to our API, we want the possibility to create a user, store all of its properties in a database and modify these properties once we created it. For this purpose we need to include the Spring Data dependency in the pom.xml file.

In order to achieve the behaviour mentioned above, we first create a model of the user, a class basically, that we have to annotate accordingly with the annotations provided by the JPA. We can now choose which properties we want to be represented by a column, what the column should be named or if the property should be unique for example. Eventually Spring Data will create a new table from our user model, so everything we can configure a SQL table to look and behave like, we can do with our model as well.

As we want the ability to store the created model in a database, we extend Springs JpaRepository interface with our own interface MovieUserRepository and annotate it with @Repository. Through this repository we can tell Spring by which properties we want to be able to select and find users in the database. As long as we provide the right method signature, Spring will magically implement the methods to work as intended.

Now, as we obviously cannot instantiate interfaces and instead want spring to implement the methods of our repository, we make use of a service that handles database interactions for us. Thus, we create a class, annotate it with @Service and autowire an instance of the MovieUserRepository (implemented by Spring) into it by using one of Springs powerful annotations @Autowired. Spring will inject the MovieUserRepository at runtime. All we have to do now is to create some methods to act on our repository which will then further interact with the database. Methods that save or delete Users from our database additionally need to be annotated with @Transactional.

In order to get all of this to work, we still need to tell Spring how to connect to our database. We do this by setting some properties in the application.properties file. Apart from url, username and password we give Spring the driver of our database. It is important to add the driver as dependency to our pom.xml, otherwise Spring doesn’t know where to look for it. As we are using an oracle database, this is the driver we need to include:

In addition, we need to tell Spring to update the database, based on our model, and create a table for it.

As you can see, by using Spring Data we are completely disconnected from any specifics of different databases and could easily switch between databases just by providing a corresponding driver.

To finally see all of this in action, let’s add a signUp() method to our REST controller and map it to a POST request on “/account”. In order to interact with the database we need access to the MovieUserService inside the controller. We achieve this by autowiring the service into the controller by annotating it with @Autowired. Additionally, we create a new SignupResponse model so we can return a useful message alongside a property success, which indicates, if the operation was successful or not.

The controller now looks like this:

Even though the implementation above already works, it still needs some improvements, such as checking if the parameters username and password are empty or match a specific pattern, if the user already exists in the database as well as better exception handling. But as this is not a very Spring related matter, it is left to you to hammer out the details to match your requirements.

Step 4: Adding Authentication

In order to implement authentication for our users, we found JWT tokens to meet our requirements. Authentication via JWT is not only an industry standard, but it allows our API to be stateless as well. The behaviour we want is as follows: A user retrieves a JWT token by providing his username and password. From now on, all requests to the API he makes include a header with the token. Thus every request handler of our API cannot only be sure that the request is indeed made by an authorized user, it can retrieve the username from the token as well.

We achieve all of this by adding Spring Security to our project dependencies and customizing some of its configurations as well as adding some filters to handle the JWT tokens.

First, we need to extend Springs WebSecurityConfigurerAdapter to create our custom SecurityConfiguration class. Here, we can configure which filters we want to apply to which requests, as well as some other security related settings such as CORS handling.

Furthermore, we provide a CustomAuthenticationProvider which extends Springs AuthenticationProvider to manually check if the credentials a user wants to authenticate with, match the ones stored in the database.

To finally get to the JWT token handling, let’s look at two filters we declare: At first, let’s declare a few constants we are going to use.

And add the libraries we are using for generating and parsing JWT tokens.

Now we are going to create the JwtAuthenticationFilter which extends Springs UsernamePasswordAuthenticationFilter. This filter is given a specific URL and method to filter on, so whenever a user wants to receive a JWT by sending a request with his username and password to this URL, the filter attempts to authenticate the user by calling the attemptAuthentication() method. Depending on whether the authentication succeeds or not, it calls the appropriate methods to handle the situation. If the authentication is successful it then generates the JWT token and returns it in the response.

The second filter filters every request but the ones we explicitly excluded (POST on /account) in the SecurityConfiguration and checks if they contain a header with a valid JWT token. If the request passes the filter, it moves on to the REST Controller. Otherwise a 403 Forbidden response code is returned.

Note, that we do not have to call all those methods ourselves, instead we just provide the implementations of what we want to happen if a user attempts to authenticate, for example. Usually we just have to return null, if we want the authentication to fail. Spring then acts accordingly and calls the unsuccessfulAuthentication() method we implemented. Furthermore, we almost at all times have the possibility to interact with the request and the response in order to get or set headers as well as status codes, for example.

Step 5: Hashing the password

Now that we can create and authorize users, we need to address some security concerns: As you may already have noticed, we are storing all of our users data in plain text, even the passwords. What we really want is to only store a hash of the password. Fortunately, we can use Spring Security’s built in BCryptPasswordEncoder for this purpose. All we have to do is modify the MovieUser class a little, so if a password is set, it gets hashed automatically. In addition, we provide a matchPassword() method, to determin if a password matches the stored hash.

In order for our authentication to work with the hashed passwords, we need to modify the CustomAuthenticationProvider class as well, so it uses the newly implemented matchPassword() method.

Now that you know the basics of building a RESTful API with Spring you can get creative yourself and extend the examples above to fit your needs. Once you’re happy with your implementation we can move on to building the frontend of our application.


Writing the Frontend using Angular

Once the REST API is in place we can start to write our frontend.

Setup

First we need to install the angular CLI.
$ npm install -g @angular/cli
Then we can generate a template for our project
$ ng new movie-client
The CLI will ask you a few questions. Say yes to angular routing and pick your style sheet format of choice.

Once it’s done generating you can try to run it
$ cd movie-client
$ ng serve

If you navigate to http://localhost:4200/ you should now see your app running.

Getting Started

To avoid having to write a lot of CSS ourselves we’ll use Bootstrap. Open your index.html file located at movie-client/src/index.html and add the following lines inside the <head>tags. They will load Bootstrap and jQuery.

Next we’ll change the root component HTML template located at movie-client/src/app/app.component.html. All our future pages will be nested inside this template so we can add the elements all our pages should have in common, in this case a simple navbar.

We’ll also add a global rule to get a red background, to do this open your global style sheet at movie-client/src/style.scss and add this rule

If you refresh your website now it should look like this.

If you click on the links in the navbar you’ll only get an error in the console since we don’t have any routes defined yet, but we’ll get to that next.

Our first Page

Next we want a way to log in, but to do that we need to talk to our API. For that purpose we’ll generate a service.
In Angular Services are used for tasks like fetching or processing data that will get used by different components.

The Angular CLI can generate a service template for us like this.
$ ng generate service backend
This will add two files to you src/app directory, for now we only care about backend.service.ts

Initially we need two functions, signup() to create an account, and authenticate() to get a JWT token.

For this to work we need to model LoginResponse. Generate a template for it with
$ ng generate class LoginResponse
and edit login-response.ts to fit the data you get from your API.

Now we can import it in our backend service

With that done we can write our login page. Start by generating a component for it.
$ ng generate component login
To remember JWT tokens we will store them as a cookie, to deal with that we need to install a service.
$ npm install ngx-cookie-service --save
To be able to use it we need to add it to the providers in app.mopdule.ts. Also add HttpClientModule and FormsModule to imports since we will need them for our login page.

Now we can edit our login component template. Open login.component.html and add a form with fields for username and password and two buttons, one to submit the form and one to navigate to a sign-up page. This template will be inserted at the <router-outlet> tags in the root template.

With the HTML template done we need to write the corresponding code in login.component.ts. The onSubmit() function gets called when we click the submit button, username and password get set by the form, and failedMsg is displayed if the login fails.

Finally we need to hook this component up to our router. To do that open app-routing.module.ts and add login to your routes like this

If you navigate to http://localhost:4200/login now you will see the page we just created. You will notice the page looks a bit wonky, but we can easily fix that with a bit of CSS. Add these rules to login.component.scss and it should look much better.

the finished login page

Improving the Navbar

Next we’ll change the navbar in the root component to take advantage of our new login cookie.

Open app.component.ts and add a constructor as well as loggedIn() and logOut() functios.

Equipped with these functions we can update our navbar to allow the user to log out. Remove the static link to login and replace it with a conditional element. It will show the link to login if the user is not logged in, and otherwise a drop-down with multiple options.

Right now only the logout() button works, but we can implement the other pages later.

our new and improved header

Now you can try to write a sign-up page on your own. Just repeat the steps we used to create the login page, but use backendService.signup() instead of backendService.authenticate() and adjust the html template a bit.

Loading and displaying Movies

All of the work we’ve done so far was only housekeeping. Now we can start to load actual content from our API.

First let’s get popular movies and display them on our currently empty homepage. To accomplish this we need to implement a few models from our API as classes, generate two components and add a function to our BackendService.

Let’s start by generation the component that will be our homepage
$ ng generate component homepage
The HTML template for this component is very simple. Just three buttons to sort the loaded movies. The movies themselves will be in the MovieGrid subcomponent.

homepage.component.ts isn’t much more complicated. We load some movies with the help of our BackendService and declare a few functions to sort them.

We will also need to implement getTrending() in our BackendService

and write classes for Search and Movie
$ ng generate class Search
$ ng generate class Movie

Search should look like this

and Movie like this

To complete our homepage we need the MovieGrid component. It does exactly what it says on the tin, display movies as a grid.
$ ng generate component MovieGrid
This is the html template for it. ngFor iterates over a list of movies and repeats for every item.

We also need to add an input decorator to the .ts file so we can get the movies from the parent component.

Finally we just need to add an entry for HomepageComponent to our app-routing.module.ts routes array.

Now we can see a grid of movies on our homepage if we are logged in, but the styling needs some polishing. The following is my SCSS for MovieGrid

and we can also add these rules to our global styles.scss

The new homepage

The next step will be to add search functionality to our website. To do this we can modify the navbar again.

Add this inline form to app.component.html just before the <nav> tag closes.

Then we need a package to base64 encode search strings so we can use them in URLs.

Add the function onSubmit() that gets called when we click search and an import statement for -js-base64 to app.component.ts.

As you can see this navigates the user to a search page.
You should now be able to implement the search page on your own just like our homepage.
Generate a component for it and add it to the router like this

To get the search string from the URL and execute the search you can use this function that gets called when the page loads.

Finishing the Website

You have probably already noticed that we have lots of links that lead nowhere. With all the major building blocks in place and you can now start adding pages on your own to finish the website.

For example here is a MovieDetails page we implemented.

Docker

To simplify deployment we will run our servers in docker containers. If you’re not familiar with docker yet, you can get up to speed with this workshop.

Frontend

Create a Dockerfile in the root of your angular project. We will create an image using docker’s multistage build functionality

That’s it, now you can build and run it it

Backend

First we build our server manually
$ mvn package

then we can use the resulting jar in our Dockerfile

You can use the same commands as before to build and run the image, but make sure to change the ports you use.

Docker Compose

Now that we have those images we can run them with Docker Compose. create a docker-compose.yaml file and add your images as services.

This allows you to start both services with just one command.
$ docker-compose up

HAProxy

HAProxy is a powerful reverse proxy that we can use to route incoming traffic to our frontend or API servers respectively. Additionally it enables us to run multiple instances of our services and manages the load balancing between them for us.

Here is a diagram of the end result we are looking for.

We need a haproxy.cfg to get HAProxy running

Save the config file in a new folder called proxy and then we can add HAProxy to our docker-compose.yml file.

We simply need to add the following service.

Then adjust the existing services we created earlier. They now need a link to and depend on the proxy service. Add two instances of each of movie-server and movie-client.

Now we can quickly and easily deploy our application in any environment.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.