Spring Boot application
These are two applications that are connected but are source-controlled, developed and deployed separately.
The Spring app uses a h2 database, the main application sets up some initial data, using a model and a repository. We also have a logging endpoint to collect client-side Angular logs.
Create a normal Spring Boot app with the Initializer web tool or in STS with the following dependencies:
- spring-boot-starter-data-jpa
- spring-boot-starter-data-rest
- spring-boot-starter-web
- spring-boot-starter-test
- h2
And optionally:
- spring-boot-devtools
- lombok
The Java code goes under src/main/java in the package com.backtojavaland and contains only 4 files.
- Application.java
- User.java
- UserRepository.java
- Log.java
//Application.java
package com.backtojavaland;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
//bootstrap database at start of application
public void run(String... strings) throws Exception {
userRepository.save(new User("Ada","Lovelace"));
userRepository.save(new User("Grace","Hopper"));
}
}
Here we’re using CommandLineRunner to add a few lines to the database. An alternative way would be to create schema.sql and data.sql files. I’d do this if using a MySQL database. See https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html.
//User.java
package com.backtojavaland;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String surname;
public User() {}
public User(String name, String surname) {
this.name = name;
this.surname = surname;
}
//getters and setters
}
@Entity, a simple User domain object, we need no-arg constructor for JPA.
//UserRepository.java
package com.backtojavaland;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(collectionResourceRel = "user", path="user")
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
}
This is just magic! We use the PagingAndSortingRepository interface to handle CRUD operations for us. The @RepositoryRestResource annotation allows us to optionally customize the REST endpoint. It defaults to the name of the domain object, and we just reiterate that here.
Now lets add a simple logging endpoint that just prints things out for now to the console:
//Log.java
package com.backtojavaland;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Log {
@PostMapping("/logs")
public void getLog(@RequestBody String request) {
System.out.println(request);
}
}
#application.properties
# make sure all REST endpoints exposed by spring boot app is under /api url
server.servlet.context-path=/api
# H2 Web Console (H2ConsoleProperties)
spring.h2.console.enabled=false # Whether to enable the console.
spring.h2.console.path=/h2-console # Path at which the console is available.
spring.h2.console.settings.trace=false # Whether to enable trace output.
spring.h2.console.settings.web-allow-others=false # Whether to enable remote access.
Build and runs on http://localhost:8080 and has two endpoints:
- /users – has ?page, ?size and ?sort options
- /profile – has metadata for application
Spring Data REST automatically follows the principles of HATEOAS too, so the content-type returned is application/hal+json.
As we already have some data in the database, we can search for them:
http://localhost:8080/users/search/findByName?name=test
There are lots of methods provided by the PagingAndSortingRepository that we can use ‘out-of-the-box’. See https://www.baeldung.com/spring-data-repositories for a quick overview.
Angular application
Create an angular application called angular-frontend.
Add a new file called proxy.conf.json:
{
"/api":{
"target" : "http://localhost:8080",
"secure" : false
}
}
Modify package.json to load up the proxy-config file.
You’ll need to use npm start to pick this file up in future!
"start": "ng serve --proxy-config proxy.conf.json",
Now add the application code.
//app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { LoggerModule, NgxLoggerLevel } from 'ngx-logger';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
LoggerModule.forRoot({serverLoggingUrl: '/api/logs', level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.ERROR}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Notice that we’ve added a logger and set it up to log to the server. We have a rest endpoint that just prints this out on the console for now.
//app.component.ts
import { Component } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [NGXLogger]
})
export class AppComponent {
title = 'frontend';
users;
constructor(public http: HttpClient, private logger: NGXLogger){
this.logger.debug('Your log message goes here');
this.logger.debug('Multiple', 'Argument', 'support');
this.getUser().subscribe(
res => this.users = res._embedded.user,
error => console.log(error)
);
}
getUser(): Observable<any>{
return this.http.get('http://localhost:4200/api/user').pipe(
map(respnse => <string[]> respnse)
);
}
}
Notice I’ve put a few logging comments in here. Angular logs to the client by default.
And then to see the data, a html file:
<h1>
Hi! {{ title }}
</h1>
<ol>
<li *ngFor="let user of users">
{{user.name}}
{{user.surname}}
</li>
</ol>
To run the applications they need to be deployed separately. The Spring Application is launched on http://localhost:8080 and the Angular application on
http://localhost:4200. The angular app talks to the spring boot application via REST and gets the data.

Notice the logging messages on the client. We see these by using the browser development tools.

I’m really happy with this example, it’s a great starting point. Now, let’s make it pretty by adding Bootstrap.