Building a CRUD Application with ag-Grid — Part 2

Sean Landsman
AG Grid
Published in
6 min readNov 7, 2017

In Part 2 of this Series, we go into more detail into the Middle Tier, exposing our data via a REST service.

Series Chapters

  • Part 1: Introduction & Initial Setup: Maven, Spring and JPA/Backend (Database)
  • Part 2: Middle Tier: Exposing our data with a REST Service
  • Part 3: Front End — Initial Implementation
  • Part 4: Front End — Grid Features & CRUD (Creation, Updates and Deletion)

Introduction

The completed code for this blog series can be found here (once the series is complete), with this particular section being under Part 2

One part of this entry of the series have been updated as the result of a bug I found later on.

Look for the Updated! note (like this one) - its in AthleteController section.

The lesson here? Write more tests folks!

In order for our data to be useful we need to make it available to users. One of the easiest ways to do that is to expose it as a REST Service, which ties in nicely with CRUD operations — there is a one to one mapping with each of the Create, Retrieve, Update and Delete operations that CRUD specifies.

We’ll again make use of Spring to expose the data via a REST service. We do this using Spring, doing so by hand would be tedious and involve a great deal of boilerplate.

Rest Controllers

We can provide REST services via Rest Controllers. In most of the services we’ll expose we’ll generally just pass through to one of the methods provided by our Repositories. In a real world application you'd want to provide authentication of user requests and so on.

To define a RestController all we need to do is annotate a class as follows:

@RestController
public class AthleteController {

We want to return all the Olympic Data we have in the first pass and in order to do that we need to use our AthleteRepository. Let's create a constructor that will take this repository (Spring will automatically inject it for us) and create a method that uses it:

private AthleteRepository athleteRepository;public AthleteController(AthleteRepository athleteRepository) {
this.athleteRepository = athleteRepository;
}
@GetMapping("/athletes")
public Iterable <Athlete> getAthletes() {
return athleteRepository.findAll();
}

What the GetMapping does is provide a mapping to our application via a URL.

In our case the full mapping would be:

http://localhost:8080/athletes

The base URL (http://localhost:8080) can vary based on how you deploy your application, but the actual mapping (/athletes) would be the same.

As you can see from our implementation we’re simply delegating down to the repository in this case, returning the data it provides. Again in a real world application you might want to secure your information in some way.

(Scroll down to see the full source code for AthleteController.)

With this in place if we now start our application and navigate to http://localhost:8080/athletes in a browser you should see something like this (formatted here for clarity):

[
{
"name": "Robin Beauregard",
"country": {
"id": 33,
"name": "United States"
},
"results": [
{
"age": 25,
"year": 2004,
"date": "29/08/2004",
"gold": 0,
"silver": 0,
"bronze": 1,
"sport": {
"id": 112,
"name": "Waterpolo"
}
},
{
"age": 21,
"year": 2000,
"date": "01/10/2000",
"gold": 0,
"silver": 1,
"bronze": 0,
"sport": {
"id": 112,
"name": "Waterpolo"
}
}
]
}
...further records

Great, so far so good!

Updating Data

So far we’ve been concentrating on getting data out of our database. Let’s switch focus to the remaining Create, Update and Delete operations that CRUD provides.

Before we can do so we need to consider what sort of updates we want to perform in our application.

For our purposes we’re going to implement the following sorts of updates:

Type of UpdateParameter(s) RequiredCreate a new Result for a given AthleteAthlete's ID, new Result object to be createdUpdate an existing ResultResult ObjectUpdate multiple existing ResultsList of Result ObjectsDelete an existing ResultResult ID

There are of course many more types of updates you might wish to perform. The above should however illustrate the different types of update which you can then use in other scenarios.

Let’s take a look at what operations the CrudRepository provides for us:

public interface CrudRepository <T, ID> extends Repository<T,ID> {
<S extends T> S save(S s);
<S extends T> Iterable<S> saveAll(Iterable<S> iterable); Optional<T> findById(ID id); boolean existsById(ID id); Iterable<T> findAll(); Iterable<T> findAllById(Iterable<ID> iterable); long count(); void deleteById(ID id); void delete(T t); void deleteAll(Iterable<? extends T> iterable); void deleteAll();
}

As you can see, just about any operation you’re likely to need has been provided here, which is great.

In our simple application however, we can map our user cases to the following two methods:

Type of UpdateCrudRepository MethodCreate a new Result for a given Athlete<S extends T> S save(S s);Update an existing Result<S extends T> S save(S s);Update an existing Athlete, supplying a "detached" version*<S extends T> S save(S s);Update multiple existing ResultsIterable<S> saveAll(Iterable<S> iterable);Delete an existing Resultvoid deleteById(ID id);

* A detached version being a version Spring/JPA/Hibernate is not currently aware of. This will be the case when we pass in an existing Athlete via the REST service, later.

In an application with more real-world requirements you’d almost certainly make use of some of the other methods provided.

As all four operations affect existing Athlete objects, we'll expose these operation on the AthleteController(although you might want update Results directly).

AthleteController

Added a new test to test the scenario where we supply a detached Athlete in to be updated/saved.

The completed controller is pretty simple — each of the calls for the above requirements simply delegates to the AthleteRepository.

Again, in a real-world application your controller would probably delegate to a Service that might perform some business specific logic.

Testing Our Progress

We’ll make use of the supplied packaged jUnit testing framework. The Spring Boot project we downloaded already includes a single test class: CrudAppApplicationTests. Let's rename this to AthleteControllerTests and empty the contents of the class.

We’ll start off with a simple test that confirms that we have the expected number of Athlete records in our database. As our controller is nothing more than a REST Service with little logic within we can make use of the Spring supplied TestRestTemplate.

This class makes it very easy to perform high level REST calls, which is exactly what we’re after here.

Note that in a real world application you’d almost certainly want to write unit tests all the way up to integration tests. In this guide we’re focusing more on illustrating how to do front to back end with ag-Grid, so will not delve too deeply into the testing side of things.

// makes the rest call, converting the results within the response body to an array of Athletes
ResponseEntity<Athlete[]> response = restTemplate.getForEntity(createURLWithPort("/athletes"), Athlete[].class);
// unpack the result
Athlete[] athletes = response.getBody();

The rest of the tests follow the same pattern — the completed AthleteController and AthleteControllerTests can be seen here:

With all of that in place, we can now run the test as follows:

mvn clean test[INFO] Results:
[INFO]
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] -------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] -------------------------------------------------------
[INFO] Total time: 14.372 s
[INFO] Finished at: 2017-11-30T15:49:20Z
[INFO] Final Memory: 37M/383M
[INFO] -------------------------------------------------------

Great, so far so good. We’ve verified that the middle tier code we’ve written for the 4 use-cases above will work once invoked by the front end!

Summary

We have now completed our backend and middle tier implementation, all ready to make a start on the front end!

In the next part we’ll complete the scaffolding for our Angular application, including displaying our data in our first grid.

See you next time!

Learn more about AG Grid — high performance JavaScript Data Grid. Our mission is to write the best data grid in the world. We write the code to visualise data in interactive tables and you can concentrate on writing the best application code in the world. With support for multiple frameworks: Angular, Vue, React, pick the best framework for your needs.

We do the work so you don’t have to.

--

--

Sean Landsman
AG Grid

Lead Developer — Frameworks. Responsible for integrating all of ag-Grid’s supported frameworks (Angular, Vue, React etc)