Finally, the time has come where we can connect the last few dots, and get the RMS up and running, accepting requests from a client, and seeing the everything you have done until now, come to life!
Now that the Service layer has been completed, we need to have a way to accept requests from the outside world, so that depending on the request, we can trigger each of the different CRUD flows to perform actions for our customers. Before we get started, we should probably go through some basics about how our the RMS can accept requests in the first place (do skip to the next part if you already know this stuff! π)
A very common way that we can send requests from one place to another is using REST over HTTP.
HTTP - defines a set of request methods to indicate the desired action to be performed for a given resource, which we can make use of to fulfil the operations that need to be completed.
Operation | HTTP Verb | HTTP Use Case | Our Use Case |
---|---|---|---|
Create | POST | Create a resource | Creating a reservation |
Read | GET | Fetch a rosrouce | Retrieving a reserivation |
Update | PUT | Update a resource | Updtaing a reservation |
Delete | DELETE | Delete a resournce | Deleteing a reservation |
REST - Representational State Transfer. This provides a standard between services, making it easier for services to communicate with each other. Back in the day, when you requested a webpage, sometimes it would look like http://www.exmaple.com/asdkafuliofiualdhdajskd
, which to the naked eye means absoluately nothing.
REST on the other hand changes this to help us give our enpoints a little more meaning! e.g. http://www.exmaple.com/api/v1/reservation
. This makes so much more sense, its clearly readable, and its easy to understand that we are interacting with v1 of this API and we are interacting with the reservation
endpoint. This is what REST attempts to solve. So in Part 1, when I said we are building a RESTful application, now you know what it means π.
REST over HTTP - combining the two, REST over HTTP basically means that we are going to use the various HTTP verbs and combine them with these nice readable URI style, to make it easy for others to interact with our API.
In our ReservationRepository
layer, we made use of the CrudRepository
interface, which has a collection of different CRUD methods that we made use of.
The Controller Layer- contins various entry points to our system. It will accept these various HTTP requests, so that we can then interact with the Service layer, then the Repository layer and in turn perform actions on the database based on the request that has come into the app. and them BOOOOOM! we are done!
Still with me?π I know it's a lot of information, but this is key information that you need to have to understand how Spring will handle your requests! I will make a post to explain this in more depth, as this stuff is important to know if you want to be better than the average Java Spring developer π.
controller
within com.rms.reservationservice
.ReservationController
. This will contain all the contoller methods that will let us trigger the Create, Read, Update and Delete flows in the ReservationService
, based on the HTTP request type and the endpoint provided.package com.rms.reservationservice.controller;
import com.rms.reservationservice.service.ReservationService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/reservation")
public class ReservationController {
public final ReservationService reservationService;
public ReservationController(ReservationService reservationService) {
this.reservationService = reservationService;
}
}
@Service
and @Repository
. This will allow other Spring beans to be injected into this class.@RestController
is also aconvenience annotation that combines @Controller
and @ResponseBody
β which eliminates the need to annotate every request handling method of the controller class with the @ResponseBody annotation./api/v1/reservation
as a path prefix. So all requests will look like http://www.example.com/api/v1/reservation
as a minimum.To complete our various reservation flows, we need controller methods that will interact with the ReservationService. As such, we can inject the ReservationService dependency into the controller directly, so that we can start to make use of the methods that the interface has to offer. OH YEAH, IT'S ALL COMIN' TOGETHER NOW! Let's break each of our flows down one by one:
When we think of "saving a new reservation", this can also be thought of as "creating a new resource", because at the end of the day, we are taking a reservation request, and when we save it to the database, we are creating a new row in our database table.
Can you remember which HTTP verb you would use for this type of request?
That's right! its a POST request!π So lets get this put into code:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.HttpStatus;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Reservation save(@RequestBody Reservation reservation) {
return reservationService.saveReservation(reservation);
}
reservation
object that has entered the controller, and delegate this to the reservationService
. Once the save has completed successfully, return back a the new reservation
object, which will be converted back into JSON and delivered back to the client.(Remember, thats the role of @ResponseBody
)If you did get stuck at any point, please checkout branch part-5-implementing-POST-save and compare your code against mine.
We have just created our first full flow of execution! ππππ Let's try it out!
Lets start up our application and lets send a POST request with some reservation data, and lets see a reservation being created. For the demo, let's use the POSTMAN HTTP client:
(make sure that you create your request request body in the same way I have before hitting the send button):
Lets look more at the response given back to us (in the green box) :
id
field - a unique ID that lets us identify the reservationduration
field - indicating that since only 2 guests are coming, they can only stay a maximum of 1 hour.Imagine a customer has already made a booking and wants to see their reservation that they had made. We want to GET this reservation so that the client can see the reservation. But how can we get the reservation for the customer? We need some unique way of identifying that specific customer booking.
Can you remember which HTTP verb you would use for retrieve a reservation?
Thatβs right! its a GET request! π Lets get this put into code:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.http.HttpStatus;
@GetMapping("/{reservationId}")
@ResponseStatus(HttpStatus.OK)
public Reservation get(@PathVariable("reservationId") String reservationId) {
return reservationService.getReservationById(reservationId);
}
reservationId
. Note, we are passing in a path /{reservationId}
so the URL would look like http://localhost:8080/api/v1/reservation/JEBS-12345
.reservationId
as part of the URI. Note, that the path variable name reservationId
is the same as the name provided in the @GetMapping
annotion.getReservationById()
method.If you did get stuck at any point, please checkout branch part-5-implementing-GET-get and compare your code against mine.
Just like the POST
method, let't give GET
a try!
id
which we can use to now fetch the existing reservation from the databaseImagine a customer has already made a booking, and wants to update their reservation that they had made as the number of people increased from 2 to 9. We want to udpate this reservation with all the new fields so that the RMS has the most relevant information about the customer's reservation.
For this, we should use the PUT
HTTP verb.
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestBody;
@PutMapping("/{reservationId}")
@ResponseStatus(HttpStatus.OK)
public Reservation update(@PathVariable("reservationId") String reservationId, @RequestBody Reservation reservation) {
return reservationService.updateReservation(reservationId, reservation);
}
If you did get stuck at any point, please checkout branch part-5-implementing-PUT-update and compare your code against mine.
Again, let's now run our service, save something to it, and then update the reservation with new data:
Awesome! So now we have the ability to save new reservations, get them back from the database, as well as update reservations. We have now completed CRU
out of CRUD! only one final step, having the ability to delete a reservation.
if a customer needed to cancel their reservation, we should be able to do this, so that other customers can make reservations for that time.
The customer would need to provide their reservationId
, so that we can find the reservation in the database and delete it accordingly.
This means we need to use a DELETE
HTTP request!
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
@DeleteMapping("/{reservationId}")
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable("reservationId") String reservationId){
reservationService.deleteReservation(reservationId);
}
If you did get stuck at any point, please checkout branch part-5-implementing-DELETE-delete and compare your code against mine.
Again, letβs now run our service, save something to it, and then delete the reservation:
Notice how the response is empty, however we get back a HTTP 200 response, indicating that the operation was successful!
ALrighty!! We are finally done!!! Congratulations!!
We have successfully implemented a Reservation Management Service, which performs CRUD requests using REST over HTTP. Customers can now finally make and alter reservations as they like, as this is now a full service that can provides business value! You can now go and show this off to all your friends, or use it however you like.
That wasn't so hard was it!? You can see how much Spring handles for you so that you do not need to keep re-writing the same boiletplate crap code over and over again, so you can focus on getting your service built and into production.
Give yourself a pat on your shoulder, you earnt it!
The finalised codebase can be seen in the FINALISED RMS, and your code should look pretty similar to mine.
Well, currently, we have implemented all the happy path situations, when everything goes well, however, what happens when you try to perform operations on the database where a customer does not exist? or what happens if you have duplicate ID's? these are things that we need to account for which we have not compelted as yet.
In my next guide, this is exactly what we are going to be looking at, so that our service can fail more gracefully, rather than spewing lots of erros and making us sad.