Spring Boot for Cloud: REST API Development
An overview of Spring Boot framework features that are especially important in the context of Spring Cloud applications. In this article, we discuss features related to API development.
Join the DZone community and get the full member experience.
Join For FreeThere are two main ways by which microservice applications can interact with each other: REST services and messaging systems. The REST approach is the most common and will be treated in this article. We will see how Spring boot can make the development process of REST APIs fairly easy, with the use of a set of useful annotations and some implicit background behavior, like the serialization of model objects to JSON format.
Another important issue is related to the documentation and sharing of APIs, and we will see that these concerns are addressed effectively with a solution named Swagger. Integrating Swagger with Spring Boot allows us to generate the documentation from the source code, as a Swagger JSON file, and even browse and manage all the exposed REST services with a web user interface.
REST API Development
In order to develop a REST API with Spring Boot, we need to add the following dependency in the Maven POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
This way we are characterizing our project as a web application, with an embedded tomcat web server included by default. Then, making use of a set of specific annotations, we can define our controllers, which are meant to expose the HTTP REST services to the external world. We can define them by simply annotating a class with @RestController. The following step is to set the mappings that are required to define the correspondence between the class methods implementing our services and the URI paths needed to execute them by HTTP calls. We see an example in the following code snippet:
@RestController
@RequestMapping("/library")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping("/book")
public List<Book> findAll() {
return bookService.findAll();
}
@GetMapping("/book/{id}")
public Book findById(@PathVariable("id") String id) {
return bookService.findById(id);
}
@PostMapping("/book")
public Book add(@RequestBody Book book) {
return bookService.save(book);
}
@PutMapping("/book")
public Book update(@RequestBody Book book) {
return bookService.save(book);
}
@DeleteMapping("/book/{id}")
public void deleteByTitle(@PathVariable("id") String id) {
bookService.deleteById(id);
}
@GetMapping(value = "/book", params = {"author"})
public List<Book> findByAuthor(@RequestParam("author") String author) {
return bookService.findByAuthor(author);
}
}
Here we have annotated a class with @RestController and @RequestMapping("/library"). The @RequestMapping annotation specifies a prefix for the HTTP paths used to call our services. Then the class public methods are configured with other annotations aimed at implementing the HTTP REST classic calls: GET, POST, PUT, DELETE:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
Those annotations contain the final portion of the HTTP path plus one or more parameters, wherever required. The @PathVariable annotation is used to map parameters passed directly in the URI path, and defined inside curly braces in the corresponding method annotation. To map query parameters, that is to say, those passed after a "?" character in the URI, the @RequestParameter annotation can be used instead, as in the findByAuthor method in the snippet above.
As an example, to get all the books in the database we can use the following (if the application is running on our local machine), with an HTTP GET method call:
http://localhost:8080/library/book
To get a book with a particular ID:
http://localhost:8080/library/book/<id value>
Or finally to get a list of books by the author (case of query parameter)
http://localhost:8080/library/book?author=<author name>
The annotations provide an easy way to "transform" an internal implementation of a Java class in a set of services that can be called from the outside world by the HTTP protocol. Another very useful feature is the serialization of the JSON data. If we consider for instance the POST Method named add in the code snippet above, we see that it takes a parameter of type Book. But we know that the call to that method is actually made by an HTTP call with a JSON body. Spring Boot automatically converts that JSON into a Book object using a library called Jackson.
The features described above are a good base to facilitate micro-service development. A further step would be to have an effective way of sharing and communicating the structure of our REST services. In the following section, we will discuss this matter, introducing a solution named Swagger.
API Documentation and Swagger Solution
Developing a whole system in a micro-service scenario, with a number of applications interacting with REST APIs, requires keeping the consistency of the APIs involved. Swagger is a solution with a number of tools to cope with these concerns. Swagger describes the APIs by means of a JSON specification. We can define our services with the JSON format and then generate the basic code from it or generate the JSON from our code. In the integration with Spring Boot that is described here, we will use the second option.
In the previous section, we have described how we can develop the REST services exposed by a Spring Boot application, with a given set of mappings and format of input and output objects. If we consider the big picture, with our single application that must interact with a number of other applications with their own REST interfaces, possibly being developed by other teams, we understand how important would be to document and communicate the structure of those interfaces.
Integrating Swagger into Spring Boot in the context of a web application, we have the means to generate a JSON description of our REST services, that will be available by a specific URL path. We will have also a Swagger web UI available that allows us to browse the services definitions and also interact with them.
How to Integrate Swagger in Spring Boot
In order to perform the integration (we use Swagger 2 in our example) the first thing we have to do is to set the related decencies, as shown in the following Maven POM fragment:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
Then we have to enable Swagger by the @EnableSwagger2 annotation. In the following code snippet example, we do it in a specific configuration class (marked with @Configuration) and, in the same class, we define a bean of type Docket. In the Docket bean configuration, we set a group name, a basic set of information like title, description, and a Predicate object with a regular expression to catch the URL paths in which the REST services that we want to be read by Swagger are available, in our example all the paths prefixed with "library".
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket postsApi() {
return new Docket(DocumentationType.SWAGGER_2).groupName("mygroup")
.apiInfo(apiInfo()).select().paths( regex("/library/.*")).build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("REST API example")
.description("A minimal REST example with H2 in memory db as persistence layer")
.termsOfServiceUrl("http://codingstrain.com")
.license("REST API example License")
.licenseUrl("fake@gmail.com")
.version("1.0")
.build();
}
}
Due to a bug, we also need an additional piece of configuration, which we show below, supposing we are using an application.yaml file:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
Running a Sample Application
You can find a sample application, with all the configurations described in the previous sections, at the following GitHub link: https://github.com/mcasari/codingstrain.git. You can install it as a local git repository with the following command "git clone https://github.com/mcasari/codingstrain.git", and use Maven to compile and build it from the command line or use your preferred IDE.
You can run it with the following Maven command "mvn spring-boot:run". Since it is a Spring Web application it will run with an embedded web server, listening by default on the 8080 port.
We can use swagger to take a quick look and even test our API. We can use the following URL to get a JSON file with the API definitions:
http://localhost:8080/v2/api-docs?group=mygroup
As we can see from the URL above, we pass a query parameter named group with a value corresponding with the group name we defined in the Docket bean configuration. As a result, we obtain a JSON that can have some utility but is not what we can call "user friendly". As a better alternative we can use the Swagger UI by the following address:
http://localhost:8080/swagger-ui.html
In the screenshot below we can see what we obtain on the screen. We can see all the REST services we have implemented in the application, and if we expand them we can even interact with them. For instance, we can post a new book by the HTTP POST service with the path "/library/book" or get a book by the author using the available GET method that takes "author" as a parameter.
Conclusion
In this article, we have seen how Spring Boot can simplify the development of REST APIs. We have also seen how integrating a solution like Swagger can improve the communication of service definitions. This is especially important in the context of micro-service systems in which we have separate independent applications that must interact with each other and the consistency of the interfaces must be preserved.
There is another very useful feature of Spring Boot, available by the so-called Actuator dependency, that provides a set of HTTP endpoints to monitor, get statistics, and manage the application in a production environment. We will cover it in the following article: Spring Boot for Cloud - Actuator.
Published at DZone with permission of Mario Casari. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments