Implement REST API with Kotlin, Spring Boot and H2 Database

Shashir
Nerd For Tech
Published in
6 min readFeb 20, 2021

In this Part-1 article, we’ll implement a simple Gadget gallery(CRUD API’s to Create, Retrieve, Update and Delete Gadget details) REST API’s with Kotlin, SpringBoot 2.x, JPA and H2 in-memory database.

Technologies/Tools:

IDE: IntelliJ (STS/Eclipse)
Kotlin: 1.4.x
Build tool: Maven
Spring Boot: 2.4.x
Database: H2 (in-memory database)
Rest Client/Postman

Gadget gallery API’s — let’s create a sprint boot project from spring initializer which exposes 6 API’s to perform CRUD operations as below.

  • Fetch all existing Gadgets from database
  • Post a new Gadget details to Gadget table
  • Fetch Gadget data based on gadgetId
  • Update Gadget data based on gadgetId
  • Remove/Delete Gadget data based on gadgetId
  • Remove all gadgets data from database

Step-1: Spring Initializer configuration (visit the configuration link and download)

Spring app generation — https://start.spring.io/

pom.xml — dependencies (kotlin, spring-web, data-jpa, H2 in-memory database)

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Step-2: Extract zip file to local directory and import it as a maven project onto IDE

Let’s start with main class, starting point for our application.

package com.shasr.springbootkotlingadgetapp

import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SpringbootKotlinGadgetAppApplication

fun main(args: Array<String>) {
runApplication<SpringbootKotlinGadgetAppApplication>(*args)
}

Step-3: Lets create data class: Gadget.kt with 5 properties to carry Gadget details. Koltin data class by default offers setter/getter methods.

package com.shasr.springbootkotlingadgetapp.model

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.Table

@Entity
@Table(name = "GADGET")
data class Gadget(

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val gadgetId : Long,
val gadgetName : String,
val gadgetCategory : String?,
val gagdetAvailability : Boolean = true,
val gadgetPrice : Double
)

Step-4: Lets create a controller class with 6 methods. To keep it simple, here we are not getting into strict validations on the request parameters.

Lets start with GET, POST, PUT & DELETE methods

GET method to get all gadgets from database
http://localhost:8888/api/gadgets/

@GetMapping("/gadgets")
fun fetchGadgets(): ResponseEntity<List<Gadget>> {
val gadgets = gadgetRepository.findAll()
if (gadgets.isEmpty()) {
return ResponseEntity<List<Gadget>>(HttpStatus.NO_CONTENT)
}
return ResponseEntity<List<Gadget>>(gadgets, HttpStatus.OK)
}

Here we are retrieving all the gadgets from the table with “findall()” method and verifying whether table has zero or more records.

If the table has one or more gadgets, we are returning all the records with 200 status (HttpStatus.OK). For table without records — we are returning 204 status (HttpStatus.NO_CONTENT)

GET gadget details based on ‘gadgetId’
http://localhost:8888/api/gadgets/{id}

@GetMapping("/gadgets/{id}")
fun fetchGadgetById(@PathVariable("id") gadgetId: Long): ResponseEntity<Gadget> {
val gadget = gadgetRepository.findById(gadgetId)
if (gadget.isPresent) {
return ResponseEntity<Gadget>(gadget.get(), HttpStatus.OK)
}
return ResponseEntity<Gadget>(HttpStatus.NOT_FOUND)
}

Here we are retrieving specific gadget based on “gadgetId” with “findById(gadgetId)” method and verifying whether gadget with gadgetId available or not.

Method returns gadget details with 200 status (HttpStatus.OK) if its available else it return 204 status (HttpStatus.NOT_FOUND)

POST a new Gadget
http://localhost:8888/api/gadgets/

@PostMapping("/gadgets")
fun addNewGadget(@RequestBody gadget: Gadget, uri: UriComponentsBuilder): ResponseEntity<Gadget> {
val persistedGadget = gadgetRepository.save(gadget)
if (ObjectUtils.isEmpty(persistedGadget)) {
return ResponseEntity<Gadget>(HttpStatus.BAD_REQUEST)
}
val headers = HttpHeaders()
headers.setLocation(uri.path("/gadget/{gadgetId}").buildAndExpand(gadget.gadgetId).toUri());
return ResponseEntity(headers, HttpStatus.CREATED)
}

Here we are creating a new gadget with “save(gadget)” method and verifying whether gadget created successfully .

Method returns URI for a newly created resource in response header with 200 status (HttpStatus.OK). In case of empty response method return 400 status (HttpStatus.NOT_FOUND) — It’s just for demo purpose.

PUT — Update Gadget details by gadgetId
http://localhost:8888/api/gadgets/{id}

@PutMapping("/gadgets/{id}")
fun updateGadgetById(@PathVariable("id") gadgetId: Long, @RequestBody gadget: Gadget): ResponseEntity<Gadget> {
return gadgetRepository.findById(gadgetId).map {
gadgetDetails ->
val
updatedGadget: Gadget = gadgetDetails.copy(
gadgetCategory = gadget.gadgetCategory,
gadgetName = gadget.gadgetName,
gadgetPrice = gadget.gadgetPrice,
gagdetAvailability = gadget.gagdetAvailability
)
ResponseEntity(gadgetRepository.save(updatedGadget), HttpStatus.OK)
}.orElse(ResponseEntity<Gadget>(HttpStatus.INTERNAL_SERVER_ERROR))
}

We can start verifying the gadget details for the ‘gadgetId’ exists in the database prior to updating the record. On successful fetch, update the details for the gadget and return HttpStatus.OK as a response.

If the requested gadget details doesn’t exist in database, then we are returning — HttpStatus.INTERNAL_SERVER_ERROR

Remove/Delete Gadget by gadgetId
http://localhost:8888/api/gadgets/{id}

@DeleteMapping("/gadgets/{id}")
fun removeGadgetById(@PathVariable("id") gadgetId: Long): ResponseEntity<Void> {
val gadget = gadgetRepository.findById(gadgetId)
if (gadget.isPresent) {
gadgetRepository.deleteById(gadgetId)
return ResponseEntity<Void>(HttpStatus.NO_CONTENT)
}
return ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR)
}

We are invoking “deleteById(gadgetId)” method if the gadget details exists for the requested ‘gadgetId’ and returning HttpStatus.NO_CONTENT. For invalid ‘gadgetId’, we are returning — HttpStatus.INTERNAL_SERVER_ERROR (500 status)

Delete all gadgets
http://localhost:8888/api/gadgets/

@DeleteMapping("/gadgets")
fun removeGadgets(): ResponseEntity<Void> {
gadgetRepository.deleteAll()
return ResponseEntity<Void>(HttpStatus.OK)
}

We are invoking “deleteAll()” method to remove all the gadget details from the Gadget table and returning 200 status — HttpStatus.OK

Step-5: JPA Repository: to interact with H2 database

package com.shasr.springbootkotlingadgetapp.repository

import com.shasr.springbootkotlingadgetapp.model.Gadget
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface GadgetRepository : JpaRepository<Gadget, Long>

Create a simple repository interface to perform necessary operations on the Gadget table. <Gadget, Long> indicates entity name we are referring to and primary key type in Gadget entity (in out case — gagdetId type)

Step-6: We can configure H2 database configuration details in application.yml as below.

## H2 Configuration
spring:
h2:
console:
enabled: true
path: /gadget-console
settings:
trace: false
web-allow-others: false
datasource:
url: jdbc:h2:mem:gadget-db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: gadgetadmin
password: gadgetpwd
jpa:
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
use_sql_comments: true
format_sql: true

Now we run the application from the main class, we can notice table “GADGET” created with structure as below (can be verified on the console)

H2 Database
GADGET table created in H2 database

Let’s start the server and verify the application is working as expected.

POST: http://localhost:8888/api/gadgets/

Eg: Kotlin — Post API testing
Eg: Kotlin — Get All API testing
Eg: Kotlin — Get API testing
Eg: Kotlin — Put API testing
Eg: Kotlin — Get API testing

GADGET Table data prior to Delete operation

Verify Gadget table for the Gadget details
Eg: Kotlin — Delete API testing
Eg: Kotlin — Get API testing
Eg: Kotlin — GET API testing
Eg: Delete API — delete all the gadgets

GitHub:

Summary:

In this article, we created and tested a simple Gadget gallery application which exposes 6 API’s with Kotlin and Spring Boot. It allows to perform Create, Retrieve, Update and Delete operation on Gadget.

What’s Next

References:

https://kotlinlang.org/
https://spring.io/guides/tutorials/spring-boot-kotlin/

--

--

Shashir
Nerd For Tech

Middleware Chapter Lead & Java Developer — Java/Python/Spring/Microservices/Kafka/Kubernetes/OCP/Camunda/Splunk/Az Cloud https://www.linkedin.com/in/shashi999