DEV Community

Francisco Pereira
Francisco Pereira

Posted on

Creating a REST API using Spring Boot + Tests + Documentation [part 02]

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
Part 02 Settings and Database
Part 03 API Implementation
Part 04 Tests + Coverage
Part 05 Swagger Documentation

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

  • How to create the system routes
  • How to view the system through user stories
  • How to view the system through ER diagram
  • How to create the database
  • How to integrate the database with the system
  • How to create migrations using flyway
  • How to create DAO classes
  • How to create a repository to interact with the database
  • How to create DTO records

Remember that in this tutorial the following tools are used:

  • JDK 17
  • IntelliJ
  • Maven
  • Insomnia
  • MySQL database

Step 01 - Creating routes

In the first part of the tutorial we created the project using Spring Initializr and ran the server. It turns out that when accessing the URL http://localhost:8080 we come across the following response:


{
    "timestamp": "2023-01-31T18:08:42.607+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/"
}

Enter fullscreen mode Exit fullscreen mode

This response, as shown earlier, appears because we have not defined any route for our application.

So let's create our route!

Inside the src.main.java.com.simplelibrary.simplelibrary.API package we will create a new package called controller. Inside it, we will store the Controllers of the system.

Inside a controller we will have the mapping of our application's routes. Thus, when defining a route, such as "/test", when the user accesses the url http://localhost:8080/test, he will no longer find the response seen above.

The class that we will create in this controller will have the following code:

package com.simplelibrary.simplelibraryAPI.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("test")
public class SimpleRouteController {

    @GetMapping
    public ResponseEntity test(){
        return ResponseEntity.ok("it works!");
    }
}

Enter fullscreen mode Exit fullscreen mode

In the first lines we see some imports that are necessary for the project. Also note that some annotations are used, such as @RequestController.

According to Spring's own documentation, this annotation means that:

[...] Marks the class as a controller where every method returns a domain object instead of a view.

The next annotation, RequestMapping("test") indicates that all requests that have the /test mapping will be handled in this class.

The @GetMapping annotation indicates that the method below it will handle a get type request. That is, if the user accesses the URL http://localhost:8080/test (using the HTTP GET method), the method below will be called. The method itself is test:

@GetMapping
    public ResponseEntity test(){
        return ResponseEntity.ok("it works!");
    }

Enter fullscreen mode Exit fullscreen mode

If you want to know more about HTTP methods, you can visit these links: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods, https://www.w3schools.com/tags/ref_httpmethods.asp

As shown, the method returns an object of type ResponseEntity. This class represents an HTTP return, where we can also insert the return codes (2xx, 4xx, 5xx...).

If you want to know more about HTTP response codes, you can visit this link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

The ResponseEntity class already carries the possible HTTP response codes. In our case we are using the return ok which is indicated by the code 200. From this return, you can insert a body containing a message. In this case we are returning it works!".

Now when performing a GET request for the URL http://localhost:8080/test we get the following response:

Image description


Step 02 - User stories

Now that we know how to create a route, we can now see our system's scenarios from the user's point of view.

  1. As a user, I would like to register books in the system by registering the following information: id, title, author, number of pages, isbn, evaluation, status (finished or not). I would also like that the evaluation of the book should be obtained automatically from the page of this book on the site http://skoob.com.br.
  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.
  3. As a user, I would like to obtain information of only one book, identified by its id.
  4. As a user, I would like to update the information of a book, identified by its id.
  5. As a user, I would like to delete a book based on its id.

Step 03 - System modeling

From the specifications presented above, we can model our system:

ER Diagram

As you can see from the entity-relationship diagram, we will only have a single table in the database called Book and it will contain the following columns:

  • id (primary key)
  • title
  • author
  • pages
  • isbn (unique)
  • rate (nullable)
  • finished

Only the rate column can have a null value.


Step 04 - Creating the database

In this tutorial I will not be focusing on installing the database on your machine. On my machine I'm using a Docker container that contains the MySQL database.

Our database will be called small_library. So, to create the database you can run the following command in SQL:


create database small_library;
use small_library;

Enter fullscreen mode Exit fullscreen mode

By doing this, your database will be created and ready for use in the application.


Step 05 - Integration of the database with the system

Once your database is ready, we can add some dependencies to the project that will help us connect the system to the database and perform some manipulations. Therefore, in your pom.xml file you can add the following dependencies between the <dependencies> and </dependencies> tags:

<!-- ... -->

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
   </dependency>
   <dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-core</artifactId>
   </dependency>
   <dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-mysql</artifactId>
   </dependency>
   <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <scope>runtime</scope>
   </dependency>

<!-- ... -->


Enter fullscreen mode Exit fullscreen mode

The following dependencies are being added:

  • Flyway Migration: from it, we can create migrations for the database.
  • MySQL Driver: is the driver that will allow the connection between our server and the MySQL database
  • Spring Data JPA: from it we can persist data in the database from entities.
  • Validation: it is the validation service that we will use to verify that the data arriving at the server is in accordance with the established validation rules.

After that, you can reload the Maven dependencies. When running your application, the terminal will display the following message:

Terminal error

This message means that you have added the database dependencies on the system but have not added any connection properties. That is, Spring is trying to fetch the connection URL, the username and password to connect, but it is not finding it.

To change that, I recommend you close the server, and go to the application.properties file inside resources and add the following:

spring.datasource.url=jdbc:mysql://localhost:3307/small_library
spring.datasource.username=root
spring.datasource.password=root

Enter fullscreen mode Exit fullscreen mode

In the spring.datasource.url tag, we inform you the URL to access your database. In my case, I'm using the mysql database and it's running on port 3307.

Finally, in the spring.datasource.username and spring.datasource.password tags we inform the database user name and password, respectively. In my case, I fill both with root.

By doing this we can start the server again and it will run successfully


Step 06 - Creating migrations

Now that we have successfully connected to the database, we can now create the scripts to create our books table. For the versioning of these scripts, we will be using the migrations system that is provided from the flyway dependency.

For those who don't know, a migration is a versioned sql script that allows you to add or modify a table in the database. This versioning is important to have a control of the modifications that are made in the database.

Flyway documentation can be found at this link here.

In Spring Boot, migrations are inside the resources.db.migration folder and each script must be saved with the following name:

<Prefix><Version>__<Description>.sql

  • Usually in the prefix we insert the letter V, meaning version.
  • In the version, we insert the version number, starting with 1 (and it will be incremented with each new script).
  • And in the description we insert what we are doing in that script.

We can create the following migration:

V1__create-table-books.sql

And inside of it the following script:


create table books(
      id bigint not null auto_increment,
      title varchar(100) not null,
      author varchar(100) not null,
      pages int not null,
      isbn varchar(100) not null unique,
      finished tinyint not null,
      rate float,

      primary key(id)
);

Enter fullscreen mode Exit fullscreen mode

NOTE: It is highly recommended that you stop the server before creating a migration to avoid any problems.

When creating the migration, we can start the server and it will automatically run the migration, creating the table in the database:

Migration

Tables in the database

Note that in addition to the books table, the flyway_schema_history table was also created. This table will store all migrations that have already been run and what is the current version of the system.


Step 07 - Creating the DAO class

So far we have made all the necessary configurations in the database. We can now create our DAO class that will help us access the data in the database.

Therefore, inside the src.main.java.com.simplelibrary.simplelibrary.API package we will create a new package called model. And inside this, we create a class called Book:

package com.simplelibrary.simplelibraryAPI.model;
import jakarta.persistence.*;
import lombok.*;
@Table(name = "books")
@Entity(name = "Book")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
    private Integer pages;
    private String isbn;
    private Double rate;
    private Boolean finished;

}

Enter fullscreen mode Exit fullscreen mode

At first, the amount of Annotations can be daunting, but it is very easy to understand:

  • @Table(name = "books") refers to the table in the database that this class references. As we created the books table, we put this value.

  • @Entity(name = "Book") refers to the name of the DAO class - telling the Spring that it is an entity.

  • @Getter is a lombok annotation to generate getters methods for all attributes of the class.

  • @setter is a lombok annotation to generate the setters methods of all attributes of the class.

  • @NoArgsConstructor is a lombok annotation to generate the class constructor method with no arguments: public Book();

  • @AllArgsConstructor is a lombok annotation to generate the constructor method of the class with all the attributes of the class present in the arguments: public Book(Long id, String title, String author, Integer pages, String isbn, Double rate, Boolean finished );

  • @EqualsAndHashCode(of = "id") is a lombok annotation to generate equals method and hashCode generator using id as parameter.

Finally, we can also add what are the attributes of the class. We will use the columns present in the database as a reference.

Note: It is important to add the annotations @Id and @GeneratedValue(strategy = GenerationType.IDENTITY) before the declaration of the id attribute to inform that this attribute is an identifier.


Step 07 - Creating the repository

We previously created our DAO class. Now it is necessary to have a way to get the data from the database - this will be done through the repositories. A repository is an interface that inherits from the JpaRepository class. From it, we can perform manipulations on the database, considering a previously created DAO class.

To create the repository, we have to go inside the src.main.java.com.simplelibrary.simplelibrary.API package and create a new package called repository. And inside this, we create an interface called BookRepository:


package com.simplelibrary.simplelibraryAPI.repository;

import com.simplelibrary.simplelibraryAPI.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book, Long> {

}


Enter fullscreen mode Exit fullscreen mode

Only that! When doing the inheritance, we have to specify which entity it will use to get the data. In this case, it is the Book entity. We also have to specify what the type of the primary key is (in this case it is Long).


Step 08 - Thinking about input and output data

We are just a few steps away from finishing the implementation of our CRUD. Now is a good time to start thinking about how the data will arrive in our application and how we would like to return it to the user.

In previous steps, some system scenarios that we must implement were presented. One of them was the following:

As a user, I would like to register books in the system by registering the following information: id, title, author, number of pages, isbn, evaluation, status (finished or not). I would also like that the evaluation of the book should be obtained automatically from the page of this book on the site http://skoob.com.br.

We know that the identifier code (id) will be generated automatically by the system and that the evaluation of the book will be obtained through the website. Therefore, we expect that to register a book in the system, the user must inform the following fields:

  • Title
  • Author
  • Number of Pages
  • ISBN
  • Finished

Therefore, we can create a class that will be used for data transfer (DTO - Data Transfer Object). We'll go into the src.main.java.com.simplelibrary.simplelibrary.API package and create a new package called dto. And inside this, we create a record called BookRequestDTO:

package com.simplelibrary.simplelibraryAPI.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record BookRequestDTO(
        @NotBlank
        String title,
        @NotBlank
        String author,
        @NotNull
        Integer pages,
        @NotBlank
        String isbn,
        @NotNull
        Boolean finished
) {

}

Enter fullscreen mode Exit fullscreen mode

For those who are not familiar, a record is an immutable type of class, that is, from the moment we create an object and fill in its data, these will not be changed. Your getters methods are created automatically.

Note that there are some annotations that were not explained earlier. As is the case with @NotBlank and @NotNull. These annotations are used for input validation. By default, we don't want any of our fields to be empty, so we add the @NotBlank annotation before Strings (which checks if the String is empty or null) and @NotNull before other classes.

When adding this validation, if the user fails to send a field, he will see a message informing the problem.

Alright, we made the record for the request, now we are going to make a new record for the response. That is, we will treat the data that will be sent to the user:


package com.simplelibrary.simplelibraryAPI.dto;

import com.simplelibrary.simplelibraryAPI.model.Book;

public record BookResponseDTO(String title, String author, Integer pages, String isbn, Double rate, Boolean finished) {
    public BookResponseDTO(Book book){
        this(book.getTitle(), book.getAuthor(), book.getPages(), book.getIsbn(), book.getRate(), book.getFinished());
    }

}


Enter fullscreen mode Exit fullscreen mode

Note that now we will only send the following information to the user:

  • Title
  • Author
  • Number of Pages
  • ISBN
  • Rate
  • Status (finished or not).

A constructor was also created that will fill in your data from our DAO class.


What we learned in this tutorial

Well done! So far you have seen how to configure the database and integrate it with the system. You've also learned how to create DAO classes and DTO records for accessing database information and sending data, respectively.

In part 3 we will finish the construction of our REST API. Creating the service which will contain the business rule. And also the rest of the access routes to the system.

Top comments (1)

Collapse
 
mariaboyce profile image
MariaBoyce

It's an awesome paragraph for all the online visitors; they will take advantage from it I am sure. Online ipl satta