Spring Boot Data and Angular NgRx Data

Spring Boot Data and Angular NgRx Data

Alain Boudard

--

In my previous article, I talked about NgRx Data linked to a simple json server. It was an attempt to show how easy is the Angular library with a simple CRUD backend.

Update : the project moved from Angular 6 to Angular 10 without real issue

Now let’s move on a little bit with a more “project style” application. This will be a Spring Boot starter application as a dataservice backend AND as a front Angular application.

I should have some disclaimer here : There is a perfectly great project working with Spring Boot and Angular already : jHipster ! I don’t intend to do what they do here, but merely keep working with a quite simple stack, and I assume not everyone wants to have the whole jHipster in their ecosystem. Also, jHipster offers so many tools, monitoring and stuff, so no comparaison.

If you search a little bit about CRUD, Spring Boot and Angular, you can find that kind of article :

I liked this article and felt inspired to see if we can come up with something VERY simple on the same principles and using NgRx Data !

Starting Spring Boot project

This is very classical project, with H2, JPA and REST :

Once the project is up in IntelliJ for example, lets see what we can expose. We will only need a few components : a model, a controller and a repository :

First we have a POJO (/model/Todo) that could not be more simple :

@Entity
public class Todo {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

Then we have the repository (/repository/TodoRepository) :

public interface TodoRepository extends PagingAndSortingRepository<Todo, Long> {
}

By default, since we have a dependency to spring-boot-starter-data-rest, there is an automatic REST endpoint to our model : http://localhost:8080/todos. It’s working out of the box and we can CRUD, but that’s not really what I expect from “simple” services :

Well, my data is somewhere right ?

Let’s admit that there are a few tools here that can be usefull, like aditional parameters and paging … That’s just not what we need here.

So now we create a custom controller to handle cutom routes and simple json results (/controller/TodoController) :

@RestController
@RequestMapping("/api")
public class TodoController {

private TodoRepository todoRepository;

public TodoController(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}

@GetMapping(value = "/todos")
public Iterable<Todo> todos() {
return todoRepository.findAll();
}

@GetMapping(value = "/todo/{id}")
public Optional<Todo> todo(@PathVariable long id) {
return todoRepository.findById(id);
}

@DeleteMapping(value = "/todo/{id}")
public void deleteTodo(@PathVariable long id) {
todoRepository.deleteById(id);
}

@PostMapping(value = "/todo")
public Todo postTodo(@RequestBody Todo todo) {
return todoRepository.save(todo);
}

@PutMapping(value = "/todo/{id}")
public Todo putTodo(@PathVariable long id,
@RequestBody Todo todo) {
todo.setId(id);
return todoRepository.save(todo);
}
}

Reading the code we can note a few things :

  • @RequestMapping annotation to match the NgRx Data root /api
  • Specific routes mapping depending on NgRx Data conventions : /todo for single entity manipulation, /todos for list work

Alternative service root api

You can use the DefaultDataServiceConfig class from NgRx Data to declare your own root for the api :

// app-store.module.ts
const
defaultDataServiceConfig: DefaultDataServiceConfig = {
root: ''
};
@NgModule({
providers: [{ provide: DefaultDataServiceConfig, useValue: defaultDataServiceConfig }],

And in the Java Controller, get rid of the /api @RequestMapping configuration.

Now we can get rid of the spring-boot-starter-data-rest dependency, and test ou API : http://localhost:8080/api/todos. Of course, as expected, the data is the same but the structure is far “better” :

That’s what I call data ;)

The Angular front app

The application is exactly the same as in the previous article : https://medium.com/@coco.boudard/ngrx-data-and-json-server-mock-services-f4d9d76aa654

We simply want to serve the Angular app in the Spring Boot, so let’s create the /angular folder and put the root folder of the Angular Cli app.

Now a bit of configuration for Cli in the angular/cli.json file :

// angular-cli.json
"apps"
: [
{
"root": "src",
"outDir": "../main/resources/static",

The static folder will be automatically served by Spring Boot, and of course we exclude it from the git repository :

// .gitignore
/src/main/resources/static/
/src/angular/node_modules/
... and some more

Since the Angular app is served by the Java server, no need to use the proxy.conf.json configuration. We simply can build the app one shot in watch mode, and eventually include the build in the maven start script.

// command line
ng build --dev --watch

I simply wanted to show how easy that lib NgRx Data is to use in a real project environement. Next step of course, use a real database instead of H2 in Spring Data. Now, again, since it’s built on top of NgRx, you have the Redux tab in the chrome dev tools, which is a really awesome tool !

Redux tab in chrome dev tools, your entities are showing !

Explore the API

Let’s try to display the number of items in our only entity ! This should be easy right, that’s one of the goal of reactive programming, having informations on specific application state objects.

We can create a new component, that will display anywhere on the page a counter :

// command line
ng g c counter

And here is the simple code we use, an Observable from our entity service : count$ :

// counter/counter.component.ts
export class
CounterComponent implements OnInit {
counter$: Observable<number>;
constructor(private todosService: TodosService) {
this.counter$ = todosService.count$;
}
// counter/counter.component.html
<div class="alert alert-primary" role="alert">
Todo counter <span class="badge badge-secondary" [innerText]="counter$ | async"></span>
</div>

And here you have a nice and easy counter, hard to make more simple !

Sources

Many thanks

Of course, all credits to

--

--