Kotlin API — Spring Boot + PostgreSQL

Matheus Garcia
4 min readJul 23, 2020

--

Purpose of this article

To develop REST API using Kotlin with Spring Boot 2.3.1 and PostgreSQL.

Prerequisites

For this project, we are going to use IntelliJ CE, JVM 11 and docker.

Create the project

First of all, you need to install the spring initializr plugin on IntelliJ.

Creation of the project using spring initializr
Attention to project type and Language
Use HATEOAS and PostgreSQL dependencies

Import Gradle to IntelliJ

To import, you need to right-click the build.gradle.kts and then “Import Gradle project”.

Gradle file with dependencies

This Gradle file includes the exposed dependency and GSON, used to manipulate database data and to manipulate JSON, respectively.

import org.jetbrains.kotlin.gradle.tasks.KotlinCompileplugins {
id("org.springframework.boot") version "2.3.1.RELEASE"
id("io.spring.dependency-management") version "1.0.9.RELEASE"
kotlin("jvm") version "1.3.72"
kotlin("plugin.spring") version "1.3.72"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
jcenter()
mavenCentral()
}
dependencies {implementation("com.google.code.gson:gson:2.8.6")
implementation("org.springframework.boot:spring-boot-starter-hateoas")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
runtimeOnly("org.postgresql:postgresql")
implementation("org.jetbrains.exposed:exposed-core:0.24.1")
implementation("org.jetbrains.exposed:exposed-dao:0.24.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.24.1")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
springBoot {
mainClassName = "com.example.demo.DemoApplicationKt"
}

Create the controller

package com.example.demo.controllerimport org.springframework.hateoas.MediaTypes
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@CrossOrigin(maxAge = 3600)
@RestController
class AppsController(
) {
@GetMapping("/", produces = [MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE])
@ResponseBody
fun helloWorld(): ResponseEntity<Any?> {
return ResponseEntity("Hello World", HttpStatus.OK)
}
}

Run the application

After importing the grade, on the left side of your IntelliJ, in the Gradle tab, your project will be loaded and can be run using this task:

In your postman, you can do a request to your endpoint:

Let’s set up our PostgreSQL

Create a docker-compose.yml for your project:

# -------------------------- DEPENDENCIES ------------------postgres: &POSTGRES
image: postgres:10.4
# ------------------------- DEV ----------------------------postgres-dev:
<<: *POSTGRES
ports:
- 5432:5432
volumes:
- ./db/dev/databases.sql:/docker-entrypoint-initdb.d/1-dev-database.sql
- ./db/dev/roles.sql:/docker-entrypoint-initdb.d/2-dev-roles.sql

We are using two volumes: one for the database itself and another one for the roles.

databases.sql

CREATE DATABASE demo_db;

roles.sql

CREATE ROLE demo_dev_rw WITH LOGIN PASSWORD 'dev_database_passwd';GRANT ALL PRIVILEGES ON DATABASE demo_db TO demo_dev_rw;

Run PostgreSQL:

docker-compose up postgres-dev

Create your table dto file

package com.example.demo.databaseimport org.jetbrains.exposed.sql.Tableobject Demo : Table() {
val id = integer("id").autoIncrement()
val name = varchar("name", 50)
}

In my case, I put this in a package called database.

Connect your application with PostgreSQL

package com.example.demoimport org.springframework.boot.autoconfigure.SpringBootApplication
import com.example.demo.database.*
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import org.springframework.boot.runApplication
import org.jetbrains.exposed.sql.*
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {val dbUrl = "jdbc:postgresql://localhost:5432/demo_db"
val dbUser = "demo_dev_rw"
val dbPass = "dev_database_passwd"
Database.connect(dbUrl, driver = "org.postgresql.Driver", user = dbUser, password = dbPass)transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.drop (Demo)
SchemaUtils.create (Demo)
}runApplication<DemoApplication>(*args)
}

We are connecting to the database, dropping (to clean on each run) and creating the table.

Add mock data

Still, in your application file, let’s create some mock data.

package com.example.demoimport org.springframework.boot.autoconfigure.SpringBootApplication
import com.example.demo.database.*
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import org.springframework.boot.runApplication
import org.jetbrains.exposed.sql.*
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {val dbUrl = "jdbc:postgresql://localhost:5432/demo_db"
val dbUser = "demo_dev_rw"
val dbPass = "dev_database_passwd"
Database.connect(dbUrl, driver = "org.postgresql.Driver", user = dbUser, password = dbPass)transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.drop (Demo)
SchemaUtils.create (Demo)
Demo.insert {
it
[name] = "Teste 1"
}
Demo.insert {
it
[name] = "Teste 2"
}
}
runApplication<DemoApplication>(*args)
}

Create a list method on dto

package com.example.demo.databaseimport org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
object Demo : Table() {
val id = integer("id").autoIncrement()
val name = varchar("name", 50)
fun getAll(): List<Any> = transaction {
Demo.selectAll().map { it[name] }
}
}

List data on endpoint

fun helloWorld(): ResponseEntity<Any?> {val list = Demo.getAll()
return ResponseEntity(Gson().toJson(list), HttpStatus.OK)
}

That’s it!

You’ve made the basis of an API using Kotlin with Gradle, spring-boot and PostgreSQL.

Next steps

You can try to create the /:id endpoint and try to get the data using the id of the table.

Good Luck and Have Fun!

--

--