Creating a REST API using Spring Boot + Tests + Documentation [part 03]
Tags: webdev, api, springboot, java
Published: 2023-02-01 18:16:31

Hi!

I'm back in this tutorial to teach you how to develop a REST API using Spring Boot. As I said earlier, this tutorial is divided into 5 parts as shown in the following table:


|   **Part**  |       **Content**      |
|:-----------:|:----------------------:|
|   Part 01   |   [Application Startup](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-01-2262)  |
|   Part 02   |  [Settings and Database](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-02-1a4i) |
| **Part 03** | **API Implementation** |
|   Part 04   |    [Tests + Coverage](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-04-4190)    |
|   Part 05   |  [Swagger Documentation](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-05-1pd6) |

---
 
It is expected that from this third part you will learn the following:

- How to create a service containing the system's business rule
- How to create the controllers containing the system's routes

Remember that in this tutorial the following tools are used:

- JDK 17
- IntelliJ
- Maven
- Insomnia
- MySQL database

---

## **Step 01 - Creating the service**

We created the Book class repository which will allow us to perform the necessary manipulations in the database (inserting, deleting, editing and viewing data, for example).

Now we can implement the system's business rule, this implementation can be done directly in `Controller` where we will have access routes to the system. But in doing so we are not following the _Single Responsibility Principle_. Therefore, Spring Boot indicates the creation of a **service** for the implementation of the business rule.

To create the service, we'll go into the `src.main.java.com.simplelibrary.simplelibrary.API` package and create a new package called `service`. And inside of this, we create an interface called `BookService`:

```
package com.simplelibrary.simplelibraryAPI.service;

import com.simplelibrary.simplelibraryAPI.dto.BookRequestDTO;
import com.simplelibrary.simplelibraryAPI.dto.BookResponseDTO;
import com.simplelibrary.simplelibraryAPI.model.Book;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.io.IOException;

public interface BookService {
    public Page<BookResponseDTO> getAllBooks(Pageable pagination);

    public BookResponseDTO show(Long id);

    public Book store(BookRequestDTO book) throws IOException;

    public BookResponseDTO update(Long id, BookRequestDTO book);

    public void delete(Long id);

    public long count();

}

```

- The `getAllBooks` method will be used to get all the books in the database. It will receive as a parameter an object of type `Pageable` which is responsible for pagination. Later when we implement the route, we'll talk about this class. Finally, it will return an object of type `Page` that will contain the contents of the `BookResponseDTO` class.

- The `show` method will be used to get the information of only 1 book. It will receive the id of this book as a parameter and return the `BookResponseDTO`.

- The `store` method will be used to store a book in the database. It will receive the information from the `BookRequestDTO` and return the `Book`. In its implementation we will talk more about this return.

- The `update` method will be used to update the book information. It will receive the id of the book and its information (`BookRequestDTO`) and will return the `BookResponseDTO`.

- The `delete` method will be used to delete a book from the database. For this we only need your id.

- The `count` method will be used to check how many books have been registered in the database. A long value will be returned.

After creating the interface, we can now create its implementation. For that, we'll go into the `src.main.java.com.simplelibrary.simplelibrary.API.service` package and create a new package called `impl`. And inside this, we create an interface called `BookServiceImpl`.

Initially the class will have the following format:

```
import org.springframework.stereotype.Service;
import com.simplelibrary.simplelibraryAPI.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class BookServiceImpl implements BookService {

@Autowired
    private BookRepository bookRepository;

}

```

As we are creating the implementation of a service, we need to insert the `@Service` annotation at the beginning of the class. By doing this, Spring will understand that this class is indeed a Service.

And notice the `@Autowired` annotation it is used for **dependency injection**. We want to inject the `BookRepository` to be used in your database manipulation methods. Therefore, we need to inform the `@Autowired` annotation before its declaration so that Spring can be taking care of this dependency.

Once this is done, we can implement the methods that belong to the interface we created earlier:

### getAllBooks 

```
@Override
public Page<BookResponseDTO> getAllBooks(Pageable pagination){
        var page = bookRepository.findAll(pagination).map(BookResponseDTO::new);
        return page;
    }

```

The repository has a method called `findAll` which will return all the books present in the database. It can receive a Pageable object as a parameter that will contain the paging information (trust me, we'll talk more about how this object is built later).

This result, by default, will return objects of type `Book` (DAO), because in the declaration of the repository we inform in the generics that it is of type `Book`. However, we previously defined that the return to the user has to be of type `BookResponseDTO`. Therefore, we will make a `map` to call the `BookResponseDTO` constructor that will receive data from an object of type `Book` and fill in its attributes.

Finally, we will return the result that has been found.


### show

```

  @Override
    public BookResponseDTO show(Long id) {
        return new BookResponseDTO(bookRepository.getReferenceById(id));
    }

```

The `show` method is quite simple to understand.
 We will use the `getReferenceById` method that will return a `Book` type object based on its id. Finally, we do the conversion to `BookResponseDTO` by calling its constructor.


### store

```
@Override
    public Book store(BookRequestDTO bookRequest) throws IOException {
        var book = new Book(bookRequest);
        book.setRate(this.scrapRatings(book.getTitle()));
        bookRepository.save(book);
        return book;
    }

```

The `store` method is used to store a book in the database. Notice that in the first line, a `Book` object is created and passed in its constructor the `bookRequest` object. Therefore, you will create the constructor inside the `Book` class:

```

 public Book(BookRequestDTO bookRequest){
        this.title = bookRequest.title();
        this.author = bookRequest.author();
        this.pages = bookRequest.pages();
        this.isbn = bookRequest.isbn();
        this.rate = bookRequest.rate();
        this.finished = bookRequest.finished();
    }

```

Next, we make the logic for filling in the value of the book's rate. As presented in the system scenario, this attribute will be filled according to what is found on the site http://www.skoob.com.br.

To extract data from the web (web scraping) I am using the Jsoup library. You can go to your pom.xml file and import it:


```
<dependency>			
   <groupId>org.jsoup</groupId>
   <artifactId>jsoup</artifactId>
   <version>1.15.3</version>
</dependency>

```

The `scrapRatings` method will look like this:

```

private Double scrapRatings(String title) {
        Double rating = Double.valueOf(0);
        try{
            String titleEdited = title.replace(" ","-");
            Document doc = Jsoup.connect("https://www.skoob.com.br/"+titleEdited).get();
            Elements info = doc.select( "div#resultadoBusca")
                    .select("div.box_lista_busca_vertical")
                    .select("div.detalhes")
                    .select("a");
            Element firstBook = info.first();
            if(firstBook != null){
                Document book = Jsoup.connect("https://www.skoob.com.br/"+firstBook.attr("href")).get();
                Elements ratingElement = book.select("div#pg-livro-box-rating")
                        .select("span.rating");
                rating = Double.parseDouble(ratingElement.text());
            }
        }catch (Exception e){
        }
        finally {
            return rating;
        }
    }

```

I won't go into much detail about Jsoup, but basically it will access a web page and return its html content where we can get its data. 
The `scrapRatings` method will search the desired site based on the book title. And will return your evaluation. If the book is not found, your evaluation will be reset to zero.

If you want to know more about the Jsoup, you can go to this link: https://jsoup.org


Going back to the book insert method, after getting the book rating value, we can call the `save` method of the repository. This method will perform data persistence. Finally, we return the book that was created.

### update

```

@Override
    public BookResponseDTO update(Long id, BookRequestDTO bookRequest) {
        var book = bookRepository.getReferenceById(id);
        book.update(bookRequest);
        return new BookResponseDTO(book);


    }

```

The update method will look for a book based on its id (using the `getReferenceById` method) and based on the data sent by the user, it will update them inside the Book object. So let's go back to the `Book` class and add the `update` method:

```
 public void update(BookRequestDTO bookRequest){
        this.title = bookRequest.title();
        this.author = bookRequest.author();
        this.pages = bookRequest.pages();
        this.isbn = bookRequest.isbn();
        this.rate = bookRequest.rate();
        this.finished = bookRequest.finished();
    }

```

Finally, we return the response DTO.

### delete

```

@Override
    public void delete(Long id) {
        bookRepository.deleteById(id);
    }

```

The `delete` method will only call the `deleteById` method that belongs to the book repository. In this method we don't need to return anything.

### count

```
@Override
public long count(){
        return bookRepository.count();
    }

```
The `count` method will return the `count` method which belongs to the book's repository.

Well done! We finalized the implementation of the business rule from the repository. We can go to the last step which is creating the routes that will call the repository!

---

## **Step 02 - Implementing the Route Controller**

We arrived at the last step of creating our API. So far we have created our DAO class and the DTO records. The first one will serve as a base for the repository to carry out manipulations in the database. The second one will be used for data input and output.

In addition, we also created the Service that will contain the implementation of our system's business rule.

Now we can create the route controller. 

Just like we did earlier in creating a controller to present a simple message to the user when accessing a route, we'll create a robust controller.

Inside the `controller` package you will create a class called `BookController` which will have the following initial body:

```
import com.simplelibrary.simplelibraryAPI.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("books")
public class BookController {

@Autowired
private BookService bookService;


} 

```

This time we are injecting the `BookService` so we can use the methods we created earlier. If you noticed, we are importing the **interface** we created and not the implementation. But don't worry, Spring will automatically recognize the implementation we made from the interface.

We have to create the following routes:

| **HTTP Method** |    **/**    |
|:---------------:|:-----------:|
|       GET       |    books    |
|       GET       | /books/{id} |
|       POST      |    /books   |
|       PUT       | /books/{id} |
|      DELETE     | /books/{id} |



### Route to get all books:

```
@GetMapping
public ResponseEntity<Page<BookResponseDTO>> getAllBooks(@PageableDefault(size=10, sort={"title"}) Pageable pagination) throws IOException {
    var page = bookService.getAllBooks(pagination);
    return ResponseEntity.ok(page);
}

```

The route to get all the books will be accessed through a GET request on the URL http://localhost:8080/books. When performing the request, the `getAllBooks` method will be called.

Note that in the method's signature we are adding an object of type `Pageable` called pagination in the parameter. Associated with it there is the signature `@PageableDefault(size=10, sort={"title"})`, this object will allow us to define the pagination properties of my result (we passed this object inside the Service, remember?).

We are injecting the `Pageable` inside our method, we don't need to worry about creating your object or anything, Spring will do it for you! As per the project requirements we have the following:

> 2. **As a user**, I would like to get all the books registered in the system through pagination. Having only 10 books per page and sorting by title.

We are placing inside the annotation the `size` properties in which we specify how many books we want to be displayed per page. And the `sort` property which specifies what the ordering criteria will be, in this case it is `title`.

We will call the `getAllBooks` method of the Service we implemented and finally we return the `ResponseEntity` with the http code response 200, and the pagination in its body.

### Route to get the information of a single book:

```
@GetMapping("/{id}")
public ResponseEntity show(@PathVariable Long id){
    var book = bookService.show(id);
    return ResponseEntity.ok(book);
}
```

The route to get the information of a single book will be accessed through a GET request on the URL http://localhost:8080/books/{id}. When performing the request, the `show` method will be called.

Note that for us to know which book the user would like to see the information, they need to specify the id. Therefore, the value of their id is passed in the URL. For this value to be recognized, we put the `@PathVariable` annotation in the parameters of the method and then its type and the variable where its value will be stored.

We will call the `show` method of the service and return the `ResponseEntity` with the http code 200.

### Route to store a book

```

@PostMapping
    public ResponseEntity store(@RequestBody @Valid BookRequestDTO bookRequest, UriComponentsBuilder uriBuilder) throws IOException {
        var book = bookService.store(bookRequest);
        var uri = uriBuilder.path("/books/{id}").buildAndExpand(book.getId()).toUri();
        return ResponseEntity.created(uri).body(book);
    }

```

The path to store a book will be accessed via a POST request to the URL http://localhost:8080/books/. When performing the request, the `store` method will be called.

In this method, the user will send in the body of the request (`@RequestBody`) the fields that we inform in the `BookRequestDTO` record and where we put all the validations. For these validations to be "called" we need to put the `@Valid` annotation.
We are also injecting the `UriComponentsBuilder` into the parameters, which will be used in the response return.

If all validations are successful, the `store` method of the Service will be called and the `book` object will be returned.
It is a good API practice, when storing an object in the database, to add a URL to the response headers where the user can access to obtain information about the object that was created. This URL is obtained from the following snippet:

```

var uri = uriBuilder.path("/books/{id}").buildAndExpand(book.getId()).toUri();

```
Finally, it is also a good practice to fully return the object that was created in the request body. Therefore, we will not return a DTO object, but a `Book` type object. In addition to returning status 201 (created).


### Route to update a book

```

@PutMapping("/{id}")
    public ResponseEntity update(@PathVariable Long id, @RequestBody @Valid BookRequestDTO bookRequest){
    var book = bookService.update(id, bookRequest);
    return ResponseEntity.ok(book);

}

```

The route to update a book will be accessed through a PUT request in the URL http://localhost:8080/books/id. When performing the request, the `update` method will be called.

This method is similar to the `store` method, only this time the user has to pass the book's id in the URL.

After calling the `update` method of the service, we will return the updated book with http status 200.

### Route to delete a book

```
@DeleteMapping("/{id}")
    public ResponseEntity delete(@PathVariable Long id){
    bookService.delete(id);
    return ResponseEntity.noContent().build();
}
```

The route to delete a book will be accessed through a DELETE request in the URL http://localhost:8080/books/id. When performing the request, the `delete` method will be called.

This method only receives the id of the book and later the `delete` method of the service will be called where the book will be successfully deleted.

In this case, we are returning http code 204 (no content).

---
## **What we learned in this tutorial**

Well done! We have finished creating our Rest API using Spring Boot. You can use Insomnia to test the requests and see if everything is working as expected.

In the [next part](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-04-4190) of the tutorial, you will learn how to create unit and integration tests on your application!

You can find the full project [HERE](https://github.com/franciscotis/simple-library-api)