API REST Spring Boot

Curso teórico e prático
Voltar

Material complementar:


Conceito:

Curso teórico e prático para criação de projeto backend API REST, utilizando Spring Boot com Kotlin (ORM hibernate JPA DAO). Projeto com funcionalidades CRUD (Create, Read/List, Update, Delete). Banco de dados MySQL. Testes via Postman. Deploy em AWS (EC2).


API REST:

Sistema web backend que suporta métodos HTTP REST (GET, POST, PUT, DELETE), transportando dados via header/body em request - response entre dispositivos comunicados em rede, via IPs em protocolo HTTP. Framework Spring Boot possui tecnologias necessárias para implementar padrão REST em projetos Java/Kotlin, usando padrões de código OOP (Programação Orientada a Objetos) e MVC (Model-View-Controller).


Padrão MVC:

  • Model: camada de modelo/negócios responsável por implementar regras de negócio da aplicação, definição das entidades e relacionamentos, implementando comportamento e camada de dados;
  • View: camada de visão, responsável pela implementação e exibição de interface com usuário;
  • Controller: camada de controle, classe OOP responsável pelo intermédio entre Model e View, pois elas nunca comunicam-se diretamente.
    Fluxo de dados:
  1. Controller recebe requisição HTTP;
  2. Controller trata dados da requisição, interagindo com Model (se necessário);
  3. Controller prepara/envia resposta HTTP ao cliente, geralmente invocando View para tal.

Instalação das ferramentas:

Instalação de pré-requisitos em ambiente Windows.

  1. JDK 21: Acesse
    • Baixar e instalar JDK Oracle
    • Win + R, digite "SystemPropertiesAdvanced", acessar Variáveis de Ambiente
    • Aba "Variáveis do sistema", em Novo, criar/editar variáveis:
        Criar:
      • Nome: JAVA_HOME
      • Valor: Caminho da pasta com JDK instalado (Ex: "C:\Program Files\Java\jdk-21")
      • Confirmar criação OK

      • Editar:
      • Nome: Path
      • Clicar em Novo
      • Valor: %JAVA_HOME%\bin
      • Confirmar criação OK
    • Verificar instalação no CMD, com comando "java -version"
  2. Kotlin compiler: Acesse
    • Baixar e instalar Kotlin compiler (.zip) - última versão estável
    • Diretório "C:/", crie pasta "Kotlin", e descompacte o Kotlin dentro dela
    • Win + R, digite "SystemPropertiesAdvanced", acessar Variáveis de Ambiente
    • Aba "Variáveis do sistema", editar variável Path:
      • Nome: Path
      • Clicar em Novo
      • Valor: C:\Kotlin\kotlinc\bin
      • Confirmar criação OK
    • Verificar instalação: Win + R, digitar CMD, e informar comando "kotlinc -version"
  3. Gradle: Acesse:
    • Baixar e instalar Gradle: site oficial Gradle, Install, Install manually, Step 1, baixar Binary-only
    • Diretório "C:/", crie pasta "Gradle", e descompacte o Gradle dentro dela
    • Win + R, digite "SystemPropertiesAdvanced", acessar Variáveis de Ambiente
    • Aba "Variáveis do sistema", editar variável Path:
      • Nome: Path
      • Clicar em Novo
      • Valor: C:\Gradle\gradle-VERSAO\bin
      • Confirmar criação OK
    • Verificar instalação: Win + R, digitar CMD, e informar comando "gradle -v"
  4. IntelliJ IDEA (Community Edition): Acesse
  5. XAMPP: Acesse
  6. Postman Agent: Acesse
  7. Cadastro no Postman

Criação do projeto:

  1. Em Spring Initializr, selecione as dependências:
    • Project: Gradle - Kotlin
    • Language: Kotlin
    • Project Metadata:
      • Group: ubsocial.com
      • Artifact: bookstore
      • Name: bookstore (igual no Artifact)
      • Package name: ubsocial.com.bookstore
      • Java: versão do JDK instalado na máquina (21)
    • Dependencies:
      • Spring Web
      • Spring Data JPA
    • Generate (projeto será gerado, abra-o no IntelliJ)
  2. Criar banco de dados MySQL: CREATE DATABASE bookstore;
  3. 'build.gradle.kts', em 'dependencies', adicionar dependencies:
    
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    runtimeOnly("com.mysql:mysql-connector-j")
    
  4. 'src/main/resources/application.properties':
    
    spring.application.name=bookstore
    spring.jpa.hibernate.ddl-auto=update
    spring.datasource.url=jdbc:mysql://localhost:3306/bookstore
    spring.datasource.username=root
    spring.datasource.password=
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.jackson.serialization.fail-on-empty-beans=false
    server.error.include-message=always
    server.error.include-binding-errors=always
    spring.jpa.show-sql=true
    spring.jpa.properties.hibernate.format_sql=true
    server.port=8080
    
  5. Dentro do pacote principal do projeto (ubsocial.com.bookstore), criar pacote "config": ficará ubsocial.com.bookstore.config
  6. Dentro do pacote "config", criar classe WebConfig.kt para conexão com frontend Vue.js:
    
    package ubsocial.com.bookstore.config
    import org.springframework.context.annotation.Bean
    import org.springframework.context.annotation.Configuration
    import org.springframework.web.servlet.config.annotation.CorsRegistry
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
    
    @Configuration
    class WebConfig {
    
        @Bean
        fun corsConfigurer(): WebMvcConfigurer {
            return object : WebMvcConfigurer {
                override fun addCorsMappings(registry: CorsRegistry) {
                    registry.addMapping("/**")
                        .allowedOrigins("http://localhost:8081")
                        .allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
                }
            }
        }
    }
    
  7. Dentro do pacote principal do projeto (ubsocial.com.bookstore), criar pacote "model": ficará ubsocial.com.bookstore.model
  8. Dentro do pacote "model", criar pacote de entidades "entity": ficará model.entity
  9. Dentro do pacote "model.entity", criar Model (entidade) Book.kt:
    
    package ubsocial.com.bookstore.model.entity
    import com.fasterxml.jackson.annotation.JsonFormat
    import jakarta.persistence.*
    import jakarta.validation.constraints.NotBlank
    import jakarta.validation.constraints.NotNull
    import jakarta.validation.constraints.Size
    import java.time.LocalDate
    
    @Entity
    data class Book(
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        val id: Long? = null,
    
        @field:NotBlank
        @field:Size(min = 1, max = 255, message = "Título precisa ter entre 1 e 255 caracteres")
        @Column(length = 255)
        val title: String = "",
    
        @field:NotBlank
        @field:Size(min = 1, max = 255, message = "Autor precisa ter entre 1 e 255 caracteres")
        @Column(length = 255)
        val author: String = "",
    
        @field:NotNull
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
        @Column(name = "published_date")
        val published_date: LocalDate = LocalDate.now(),
    
        @field:NotBlank
        @field:Size(min = 13, max = 13, message = "ISBN precisa ter 13 caracteres")
        @Column(unique = true)
        val isbn: String = "",
    
        val pages: Int = 0,
    
        @field:Size(min = 1, max = 255, message = "Capa/Gênero precisa ter entre 1 e 255 caracteres")
        @Column(length = 255)
        val cover: String? = null,
    
        @field:NotBlank
        @field:Size(min = 1, max = 255, message = "Idioma precisa ter entre 1 e 255 caracteres")
        @Column(length = 255)
        val language: String = ""
    )
    
  10. No pacote "model", criar pacote "repositories": ficará model.repositories
  11. No pacote "repositories", criar interface BookRepository.kt:
    
    package ubsocial.com.bookstore.model.repositories
    import org.springframework.data.jpa.repository.JpaRepository
    import ubsocial.com.bookstore.model.entity.Book
    
    interface BookRepository : JpaRepository<Book, Long>
    
  12. Pacote principal "ubsocial.com.bookstore, criar pacote "controller"
  13. Pacote "controller", criar classe BookResource.kt:
    
    package ubsocial.com.bookstore.controller
    import jakarta.validation.Valid
    import org.springframework.http.HttpStatus
    import org.springframework.http.ResponseEntity
    import org.springframework.web.bind.annotation.*
    import ubsocial.com.bookstore.model.entity.Book
    import ubsocial.com.bookstore.model.repositories.BookRepository
    
    @RestController
    @RequestMapping("/books")
    class BookResource(private val bookRepository: BookRepository) {
    
        @GetMapping
        fun getAll(): List<Book> = bookRepository.findAll()
        
        @GetMapping("/{id}")
        fun getById(@PathVariable id: Long): ResponseEntity<Book> {
            val book = bookRepository.findById(id)
            return if (book.isPresent) ResponseEntity.ok(book.get())
            else ResponseEntity.notFound().build()
        }
    
        @PostMapping
        fun create(@RequestBody @Valid book: Book): Book = bookRepository.save(book)
    
        @PutMapping("/{id}")
        fun update(@PathVariable id: Long, @RequestBody @Valid book: Book): ResponseEntity<Book> {
            val optional = bookRepository.findById(id)
            if (optional.isEmpty) return ResponseEntity.notFound().build()
    
            val updated = optional.get().copy(
                title = book.title,
                author = book.author,
                published_date = book.published_date,
                isbn = book.isbn,
                pages = book.pages,
                cover = book.cover,
                language = book.language
            )
            return ResponseEntity.ok(bookRepository.save(updated))
        }
    
        @DeleteMapping("/{id}")
        fun delete(@PathVariable id: Long): ResponseEntity<Void> {
            return if (bookRepository.existsById(id)) {
                bookRepository.deleteById(id)
                ResponseEntity.noContent().build()
            } else {
                ResponseEntity.notFound().build()
            }
        }
    }
    
  14. Na mesma pasta de "gradlew", executar projeto: ".\gradlew bootRun"

Testes no Postman:

Postman é software que simula client para consumir API REST backend, através requests HTTP, usando verbos REST, respectivos métodos e header/body, e aguardando response HTTP.

  1. Teste Postman (create):
    • URL: http://localhost:8080/books
    • Método: POST
    • Body raw (JSON):
      
      {
          "title": "O Senhor dos Anéis",
          "author": "J.R.R. Tolkien",
          "published_date": "1954-07-29",
          "isbn": "9780261102385",
          "pages": 1178,
          "cover": "Ficção",
          "language": "Português"
      }
      
    • Enviar (Send)
  2. Teste Postman (get):
  3. Teste Postman (get específico):
  4. Teste Postman (update):
    • URL: http://localhost:8080/books/1
    • Método: PUT
    • Body raw (JSON):
      
      {
          "title": "O Senhor dos Anéis - Edição Atualizada",
          "author": "J.R.R. Tolkien",
          "published_date": "1954-07-29",
          "isbn": "9780261102385",
          "pages": 1178,
          "cover": "Ficção",
          "language": "Português"
      }
      
    • Enviar (Send)
  5. Teste Postman (delete):

Deploy na AWS:

Publicar projeto API REST Spring Boot na Amazon Web Services (AWS), via criação de instância EC2 (Ubuntu) gratuita.


--- CRIAR INSTÂNCIA AWS ---
EC2 / Instâncias / Executar instâncias
- Nome: prjCursoSpringBoot1
- AMI: Ubuntu Server (nível gratuito)
- Tipo: t3.micro (verificar se região é t2 ou t3 nível gratuito)
- Criar par de chaves:
-- Nome: keySpringBoot1
-- Tipo: RSA
-- Formato: conforme SO de sua máquina
Criar par de chaves (download .pem ou .ppk)
- Rede:
-- Atribuir IP público automaticamente: habilitar
-- Grupo de segurança criado automaticamente, com regras:
--- Permitir tráfego HTTP qualquer lugar
--- Permitir tráfego HTTPS qualquer lugar
--- Permitir tráfego SSH qualquer lugar
- Armazenamento: 8GiB gp3 (nível gratuito)
Criar (executar instância)

--- HABILITAR PORTA SPRING BOOT ----
EC2 / Instâncias / Clicar na instância criada
Aba Segurança / Grupo de segurança
- Editar regras de entrada
-- Adicionar regra:
--- Tipo: TCP personalizado, Protocolo: TCP, Intervalo de portas: 8080, Origem: Qualquer-local-IPv4 (0.0.0.0/0), Descrição: API REST Spring Boot
Salvar regras

--- CONECTAR INSTÂNCIA AWS ---
EC2 / Instâncias / Clicar na instância criada / Conectar:
- Habilitar Conecte-se usando um IP público
Conectar

--- INSTALAR API REST SPRING BOOT ---
Na instância CLI, executar comandos:
- sudo apt update && sudo apt install git curl openjdk-21-jdk mysql-server -y && sudo systemctl enable mysql --now
- sudo mysql
-- ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123';
-- FLUSH PRIVILEGES;
-- Ctrl+d (sair do MySQL)
- mysql -u root -p (informar senha 123)
-- CREATE DATABASE bookstore;
-- SHOW DATABASES;
-- Ctrl+d (sair do MySQL)
- git clone https://github.com/mateusschwede/workover_spring_boot_api_rest.git && cd workover_spring_boot_api_rest && cd bookstore && sudo chmod +x gradlew
- ./gradlew clean build --refresh-dependencies
- ./gradlew bootRun &
- curl http://localhost:8080/books

--- CONSUMIR API REST SPRING BOOT VIA CURL ---
- Listar books: curl http://localhost:8080/books
- Adicionar book:
curl -X POST http://localhost:8080/books \
-H "Content-Type: application/json" \
-d '{
    "title": "O Senhor dos Anéis",
    "author": "J.R.R. Tolkien",
    "published_date": "1954-07-29",
    "isbn": "9780261102385",
    "pages": 1178,
    "cover": "Ficção",
    "language": "Português"
}'

- Visualizar book específico: curl http://localhost:8080/books/1

- Editar book:
curl -X PUT http://localhost:8080/books/1 \
-H "Content-Type: application/json" \
-d '{
    "title": "O Senhor dos Anéis",
    "author": "J.R.R. Tolkien",
    "published_date": "1954-10-29",
    "isbn": "9780261102385",
    "pages": 1178,
    "cover": "Ficção",
    "language": "Português - ATUALIZADO"
}'

- Deletar book: curl -X DELETE http://localhost:8080/books/1

--- CONSUMIR API REST SPRING BOOT VIA POSTMAN ---
EC2 / Instâncias / Clicar na instância criada / Descrição / IPv4 público: copiar IP
- Pode-se visualizar IPv4 via CLI: ip addr show eth0
- Listar books: GET http://IPv4-PUBLICO-DA-EC2:8080/books
- Adicionar book: POST http://IPv4-PUBLICO-DA-EC2:8080/books
Body raw (JSON):
{
    "title": "O Senhor dos Anéis",
    "author": "J.R.R. Tolkien",
    "published_date": "1954-07-29",
    "isbn": "9780261102385",
    "pages": 1178,
    "cover": "Ficção",
    "language": "Português"
}
- Visualizar book específico: GET http://IPv4-PUBLICO-DA-EC2:8080/books/1
- Editar book: PUT http://IPv4-PUBLICO-DA-EC2:8080/books/1
Body raw (JSON):
{
    "title": "O Senhor dos Anéis",
    "author": "J.R.R. Tolkien",
    "published_date": "1954-10-29",
    "isbn": "9780261102385",
    "pages": 1178,
    "cover": "Ficção",
    "language": "Português - ATUALIZADO"
}
- Deletar book: DELETE http://IPv4-PUBLICO-DA-EC2:8080/books/1

--- PARAR INSTÂNCIA AWS ---
- Na instância, executar comandos:
-- Parar servidor Gradle: pkill -f gradle
-- Parar MySQL: sudo systemctl stop mysql
- EC2 / Instâncias / Selecionar instância / Ações / Estado da instância / Encerrar instância
- EC2 / Grupos de segurança / Selecionar grupo de segurança da instância / Excluir
- EC2 / Chaves de acesso / Selecionar keySpringBoot1.pem / Excluir

Elaborado por Mateus Schwede
ubsocial.github.io