Introdução

O desenvolvimento de APIs REST tem se tornado uma necessidade primordial em aplicações modernas. Kotlin, com sua sintaxe concisa e recursos de interoperabilidade com Java, se torna uma escolha excelente para trabalhar em conjunto com o Spring Boot, um dos frameworks mais populares. Neste tutorial, iremos explorar como integrar Kotlin ao Spring Boot para construir uma API REST eficiente, aproveitando o melhor de ambas as linguagens. Vamos abordar desde a configuração inicial do projeto até a implementação de testes unitários. No final, você terá uma aplicação funcional que utiliza Kotlin como linguagem de programação e Spring Boot para gerenciar a lógica de backend. Este é um guia prático que atenderá tanto iniciantes quanto desenvolvedores mais experientes que desejam expandir suas habilidades.

Etapas

  1. Configuração do Ambiente de Desenvolvimento

    Para começar, precisamos ter o JDK e o Gradle instalados em sua máquina. Kotlin é totalmente compatível com a JVM, portanto, certifique-se de que as versões corretas estejam instaladas. Você pode verificar as versões usando os comandos abaixo. Para uma experiência de codificação ideal, recomenda-se também utilizar uma IDE como IntelliJ IDEA.

    commands
    # Verificar versões instaladas
    java -version
    gradle -v

  2. Criação do Projeto Spring Boot com Kotlin

    Utilize o Spring Initializr para criar um novo projeto. Escolha as opções: **Project**: Gradle Project, **Language**: Kotlin, **Spring Boot**: última versão estável, **Packaging**: Jar, **Java**: 11 ou superior. Adicione as dependências Spring Web e Spring Data JPA. Após configurar, faça o download e extraia o arquivo.

    build.gradle.kts
    plugins {
        id("org.springframework.boot") version "3.0.0"
        id("io.spring.dependency-management") version "1.0.11.RELEASE"
        kotlin("jvm") version "1.6.10"
        kotlin("plugin.spring") version "1.6.10"
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation("org.springframework.boot:spring-boot-starter-web")
        implementation("org.springframework.boot:spring-boot-starter-data-jpa")
        implementation("com.h2database:h2")
        testImplementation("org.springframework.boot:spring-boot-starter-test")
    }
    
    tasks.withType<Test> {
        useJUnitPlatform()
    }

  3. Configuração do Banco de Dados H2

    No arquivo `src/main/resources/application.properties`, defina as propriedades do banco de dados H2 e habilite o console web para gerenciar os dados.

    application.properties
    spring.h2.console.enabled=true
    spring.datasource.url=jdbc:h2:mem:testdb
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
    spring.jpa.hibernate.ddl-auto=update

  4. Criação da Entidade `Produto`

    Implemente a classe modelo `Produto` que representará os produtos da sua aplicação. Utilize anotações JPA para mapear a classe à tabela do banco de dados.

    Produto.kt
    package com.example.apirestful.model
    
    import javax.persistence.*
    
    @Entity
    @Table(name = "produtos")
    data class Produto(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Long? = null,
        @Column(nullable = false)
        var nome: String,
        @Column(nullable = false)
        var preco: Double
    )

  5. Criação do Repositório `ProdutoRepository`

    Crie uma interface `ProdutoRepository`, que estende `JpaRepository` para fornecer métodos CRUD.

    ProdutoRepository.kt
    package com.example.apirestful.repository
    
    import com.example.apirestful.model.Produto
    import org.springframework.data.jpa.repository.JpaRepository
    import org.springframework.stereotype.Repository
    
    @Repository
    interface ProdutoRepository : JpaRepository<Produto, Long>

  6. Implementação do Serviço `ProdutoService`

    Desenvolva a classe `ProdutoService`, que gerencia a lógica de negócios e interage com o `ProdutoRepository`.

    ProdutoService.kt
    package com.example.apirestful.service
    
    import com.example.apirestful.model.Produto
    import com.example.apirestful.repository.ProdutoRepository
    import org.springframework.beans.factory.annotation.Autowired
    import org.springframework.stereotype.Service
    import java.util.Optional
    
    @Service
    class ProdutoService(@Autowired private val produtoRepository: ProdutoRepository) {
    
        fun findAll(): List<Produto> = produtoRepository.findAll()
    
        fun findById(id: Long): Optional<Produto> = produtoRepository.findById(id)
    
        fun save(produto: Produto): Produto = produtoRepository.save(produto)
    
        fun update(id: Long, produtoDetails: Produto): Produto {
            val produto = produtoRepository.findById(id).orElseThrow { RuntimeException("Produto não encontrado") }
            produto.nome = produtoDetails.nome
            produto.preco = produtoDetails.preco
            return produtoRepository.save(produto)
        }
    
        fun deleteById(id: Long) = produtoRepository.deleteById(id)
    }

  7. Implementação do Controlador `ProdutoController`

    Crie `ProdutoController` que exponha os endpoints da API REST para realizar operações CRUD.

    ProdutoController.kt
    package com.example.apirestful.controller
    
    import com.example.apirestful.model.Produto
    import com.example.apirestful.service.ProdutoService
    import org.springframework.beans.factory.annotation.Autowired
    import org.springframework.http.ResponseEntity
    import org.springframework.web.bind.annotation.*
    import java.util.List
    
    @RestController
    @RequestMapping("/api/produtos")
    class ProdutoController(@Autowired private val produtoService: ProdutoService) {
    
        @GetMapping
        fun getAllProdutos(): List<Produto> = produtoService.findAll()
    
        @GetMapping("/{id}")
        fun getProdutoById(@PathVariable id: Long): ResponseEntity<Produto> =
            produtoService.findById(id).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build())
    
        @PostMapping
        fun createProduto(@RequestBody produto: Produto): Produto = produtoService.save(produto)
    
        @PutMapping("/{id}")
        fun updateProduto(@PathVariable id: Long, @RequestBody produtoDetails: Produto): ResponseEntity<Produto> =
            ResponseEntity.ok(produtoService.update(id, produtoDetails))
    
        @DeleteMapping("/{id}")
        fun deleteProduto(@PathVariable id: Long): ResponseEntity<Void> {
            produtoService.deleteById(id)
            return ResponseEntity.noContent().build()
        }
    }

  8. Implementação de Testes Unitários

    Crie uma classe de testes `ProdutoServiceTest` para validar a lógica de negócios usando Mockito e JUnit.

    ProdutoServiceTest.kt
    package com.example.apirestful.service
    
    import com.example.apirestful.model.Produto
    import com.example.apirestful.repository.ProdutoRepository
    import org.junit.jupiter.api.Assertions.*
    import org.junit.jupiter.api.BeforeEach
    import org.junit.jupiter.api.Test
    import org.mockito.InjectMocks
    import org.mockito.Mock
    import org.mockito.MockitoAnnotations
    import java.util.Optional
    import org.mockito.Mockito.*
    
    class ProdutoServiceTest {
        @InjectMocks
        private lateinit var produtoService: ProdutoService
    
        @Mock
        private lateinit var produtoRepository: ProdutoRepository
    
        @BeforeEach
        fun init() {
            MockitoAnnotations.openMocks(this)
        }
    
        @Test
        fun testFindAll() {
            val produto1 = Produto(nome = "Produto 1", preco = 10.0)
            val produto2 = Produto(nome = "Produto 2", preco = 20.0)
    
            `when`(produtoRepository.findAll()).thenReturn(listOf(produto1, produto2))
    
            val produtos = produtoService.findAll()
            assertEquals(2, produtos.size)
            verify(produtoRepository, times(1)).findAll()
        }
    
        // Testes adicionais para os métodos findById, save, update e deleteById
    }

  9. Executando a Aplicação e Testes

    Use Gradle para compilar e executar a aplicação. Utilize Postman ou cURL para testar os endpoints da API.

    commands
    # Compilar e executar a aplicação
    gradle bootRun
    # Executar os testes unitários
    gradle test

    curl_examples
    # Listar todos os produtos
    curl -X GET http://localhost:8080/api/produtos
    # Criar um novo produto
    curl -X POST -H "Content-Type: application/json" -d '{"nome":"Novo Produto", "preco":99.99}' http://localhost:8080/api/produtos

Conclusão

Neste tutorial, mostramos como integrar Kotlin com Spring Boot para construir uma API RESTful eficiente. Você aprendeu a configurar o ambiente, criar entidades, repositórios, serviços e controladores, além de implementar testes unitários. A interoperabilidade entre Kotlin e Spring Boot proporciona uma abordagem moderna no desenvolvimento de aplicações web, permitindo a construção de serviços escaláveis e de fácil manutenção. Agora que você completou este guia, está mais preparado para aplicar esses conceitos em seus próprios projetos e explorar ainda mais o potencial das tecnologias Java e Kotlin.

Hashtags

#Kotlin #SpringBoot #APIs #REST #DesenvolvimentoDeSoftware