In the previous post, we created a REST API using Spring Boot to query data stored in our database. Now, in this post we will take the next step. In order to improve our sample application, we will create REST API using Spring Boot to Insert and Update Data in our database.

As per HTTP standards, Insert and Update correspond to the HTTP POST and HTTP PUT verbs. In typical RESTful standards, we treat entities as resources. POST method basically deals with creating a resource on the server. On the other hand, PUT deals with updating an existing resource on the server.

We will implement both methods in this post. So let’s start.

Defining the Data Transfer Objects

In our sample app, Vehicles is the resource. The resource will have a business representation that might also be different from the actual persistence level representation. Also, this representation might vary for queries and commands.

To handle such a situation, we use Data Transfer Objects or DTOs. Basically, these DTO objects act as bridge between the persistence layer and the interface layer.

The first one would be the VehicleCreateDTO. This DTO has the bare minimum fields required to create a vehicle in our application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class VehicleCreateDTO {
    private String vehicleIdentityNumber;
    private String make;
    private String model;
    public String getVehicleIdentityNumber() {
        return vehicleIdentityNumber;
    }
    public void setVehicleIdentityNumber(String vehicleIdentityNumber) {
        this.vehicleIdentityNumber = vehicleIdentityNumber;
    }
    public String getMake() {
        return make;
    }
    public void setMake(String make) {
        this.make = make;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    @Override
    public String toString() {
        return "VehicleCreateDTO{" +
                "vehicleIdentityNumber='" + vehicleIdentityNumber + '\'' +
                ", make='" + make + '\'' +
                ", model='" + model + '\'' +
                '}';
    }
}

Then, we will create DTO for Update. We call it VehicleUpdateDTO. Important thing to note here is that we have only two fields. Basically, we are implying here that a consumer can only update the make and model of the vehicle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class VehicleUpdateDTO {
    private String make;
    private String model;
    public String getMake() {
        return make;
    }
    public void setMake(String make) {
        this.make = make;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
    @Override
    public String toString() {
        return "VehicleUpdateDTO{" +
                "make='" + make + '\'' +
                ", model='" + model + '\'' +
                '}';
    }
}

Creating the Service Layer

Like last time, we will again create a service layer to handle the logic of inserting and updating. This time we will create a command service.

Let’s first declare the service interface.

1
2
3
4
5
6
public interface VehicleCommandService {
    public UUID createVehicle(VehicleCreateDTO vehicleCreateDTO);
    public VehicleQueryDTO updateVehicle(UUID id, VehicleUpdateDTO vehicleUpdateDTO);
}

Then, we create an implementation for this interface. In the implementation, we will actually implement the two methods declared above.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Service
public class VehicleCommandServiceImpl implements VehicleCommandService {
    @Autowired
    private VehicleRepository vehicleRepository;
    @Override
    public UUID createVehicle(VehicleCreateDTO vehicleCreateDTO) {
        Vehicle newVehicle = new Vehicle();
        newVehicle.setId(UUID.randomUUID());
        newVehicle.setVehicleIdentityNumber(vehicleCreateDTO.getVehicleIdentityNumber());
        newVehicle.setMake(vehicleCreateDTO.getMake());
        newVehicle.setModel(vehicleCreateDTO.getModel());
        newVehicle.setStatus(String.valueOf(Status.FOR_SALE));
        return vehicleRepository.save(newVehicle).getId();
    }
    @Override
    public VehicleQueryDTO updateVehicle(UUID id, VehicleUpdateDTO vehicleUpdateDTO) {
        if (vehicleRepository.findById(id).isPresent()){
            Vehicle existingVehicle = vehicleRepository.findById(id).get();
            existingVehicle.setMake(vehicleUpdateDTO.getMake());
            existingVehicle.setModel(vehicleUpdateDTO.getModel());
            Vehicle updatedVehicle = vehicleRepository.save(existingVehicle);
            return new VehicleQueryDTO(updatedVehicle.getId(), updatedVehicle.getVehicleIdentityNumber(),
                    updatedVehicle.getMake(), updatedVehicle.getModel());
        }else{
            return null;
        }
    }
}

Let’s understand what we have done here:

  • We first wire the VehicleRepository interface. The Vehicle Repository extends the Spring Data JPA’s CrudRepository interface. The CrudRepository interface has the save() method.
  • Then, we implement the methods declared in the interface. First is the createVehicle() method. In this method, we retrieve the values from the VehicleCreateDTO object. Using those values, we build the Vehicle entity object. Notice how we are hard-coding the Status field to the Enum value FOR_SALE. Basically, when the Vehicle is first created it should always have status as FOR_SALE. We didn’t expose this field in the VehicleCreateDTO because we don’t want the consumer accidentally setting this to some unacceptable value.
  • Next, we implement the updateVehicle() method. This method takes the id of the vehicle and the VehicleUpdateDTO object. To do so, we first check if we have a vehicle with the given id. If we find such a vehicle, we update the existing make and model values of the object to the new values. Then, we save the object again using VehicleRepository’s save() method.

Creating the REST Controller

The last missing piece in our application is the REST Controller to handle the incoming POST and PUT request. We will create a separate class to handle the two commands.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RequestMapping(value = "/api/vehicles")
public class VehicleCommandController {
    @Autowired
    private VehicleCommandService vehicleCommandService;
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<UUID> createVehicle(@RequestBody VehicleCreateDTO vehicleCreateDTO){
        return new ResponseEntity<>(vehicleCommandService.createVehicle(vehicleCreateDTO), HttpStatus.CREATED);
    }
    @PutMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public ResponseEntity<VehicleQueryDTO> updateVehicle(@PathVariable(value = "id") UUID id,
                                                         @RequestBody VehicleUpdateDTO vehicleUpdateDTO){
        return new ResponseEntity<>(vehicleCommandService.updateVehicle(id, vehicleUpdateDTO), HttpStatus.OK);
    }
}

Nothing drastically different here as compared to the query controller we created in the previous post.

The main difference is that this time we use @PostMapping and @PutMapping. In the case of POST, we pass the request body to the VehicleCreateDTO object. This is done using the @RequestBody annotation. Then, we use the vehicleCommandService to create the Vehicle and return the response in a Response Entity object.

Similarly, in the case of PUT, we pass the request body to VehicleUpdateDTO object. PUT also receives the id of the resource in the path variable. We can access it using @PathVariable and pass it to the service method.

Another important thing to note is the Response Status. For POST, we are using HttpStatus.CREATED. This, in turn, maps to the response code 201. For the PUT method, we use HttpStatus.OK that maps to response code 200.

And basically, that’s all there is. We should now be able to test our application.

Testing POST and PUT end-points

We were able to test the GET implementation in the last post directly from the browser. However, we can’t do the same for POST and PUT unless we use some special browser plugin like RESTED. This is because POST and PUT method have Request Body in their payload. Browser can’t handle it directly.

However, instead of using a plugin, we will use Postman. It is an industry-standard tool for testing APIs. You can get it from here.

Triggering a REST API from Postman is pretty straightforward. We provide the URL and select the HTTP method from a dropdown. Then, we will provide the body in JSON format.

See below screenshot for the POST method on our Vehicle resource. Note that we have provided the input in JSON format and selected JSON as the input type.

testing spring boot rest api using postman

Click the Send button to send the request. If everything has been correct till this point, we will see the response as below. In our case it will just be the UUID of the vehicle resource. Also, note the HTTP Status of 201 (Created).

rest api response in postman

Now, we try to update the Vehicle we just inserted. See below for the required setup in Postman.

testing rest api using postman

Here, we are providing the make and model. In the URL path we provide the UUID that we received as output to the POST call. We also selected PUT from the dropdown.

After we send the request, we receive the response JSON. In this case, we are returning all the fields. We can see that the model field has been successfully updated.

Conclusion

With this we have successfully implemented and tested POST and PUT end-points. The code for this post is available on Github.

Basically, we can now insert and update data in our database using RESTful APIs. This really increases the usefulness of our application. We can add even more end-points to the same application based on the requirement. Once the initial setup is done, it becomes extremely easy to add more functionality.

In the next post, we will enhance our application to handle exceptions. This will make it more robust and closer to production-level.


Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

6 Comments

Francislainy Campos · September 14, 2020 at 3:55 am

Hi, thanks for the great article. Really helpful!

    Saurabh Dashora · September 14, 2020 at 11:39 am

    Thanks for the great feedback!

Azar · December 19, 2020 at 5:24 pm

what is VehicleQueryDTO?

    Saurabh Dashora · December 20, 2020 at 3:14 am

    VehicleQueryDTO is simply an interface for data transfer

Abhinav · July 18, 2022 at 1:20 pm

Is there a way to save the updated data after we stop the API from running? Because I’m trying it on my PC and every time I run the API, the updated data gets removed and the old data appears again.

    Saurabh Dashora · July 21, 2022 at 1:53 am

    Hi Abhinav,
    By default the H2 database is in-memory. You could try to persist it to a file so that it can survive restarts. Look into spring.datasource.url property to point to a file instead of in memory option.

Leave a Reply

Your email address will not be published. Required fields are marked *