Blog Infos
Author
Published
Topics
,
Published
Topics
,

Kotlin & Spring Boot: Building a Rest API by Manuel Ernesto

 

Hi everyone, in this first article as part of the series about Kotlin for server side, we’ll show how to build a Rest API with Kotlin and Spring Boot in an uncomplicated way.

For this article, we’ll build a simple Rest API to store and retrieve Players information.

We’ll use MySQL as our database, with JPA and Hibernate to access data from the database.

Setup the project

We can create our spring boot application in an IDE like IntelliJ IDEA or using the website: start.spring.io.

In our case we will use the start.spring.io website to generate the project.

start.spring.io view

Fill the information about the project, add the Spring WebSpring Data JPA and MySQL Driver dependencies and click generate, or you can clone/download the starter project from my GitHub.

After downloading or cloning the project, open/import in your IDE (IntelliJ IDEA in my case).

Project Overview
Pom.xml file

Dependencies section

<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Above we can see the dependencies for our API, these dependencies allow us to work properly with Kotlin, Spring and MySQL database.

Build section

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
view raw build.xml hosted with ❤ by GitHub

In the build section of the POM.xml file we can find the plugins for Spring and JPA, and also the dependencies to allow to compile to allow open class in Kotlin, since by default all class are final, and to allow no arguments construct in Kotlin Data Classes.

Main file
@SpringBootApplication
class PlayersApiApplication
fun main(args: Array<String>) {
runApplication<PlayersApiApplication>(*args)
}

In the main file, we can see the class annotated with @SpringBootApplication and main method for running the spring application.

Time to code
Config the Database

We’ll configure the database for our API, for that, in the application.properties file, put the configurations below.

spring.jpa.database=mysql
spring.datasource.url=jdbc:mysql://localhost/dbplayers
spring.datasource.username=db_username
spring.datasource.password=db_password
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

Let’s see what each line means:

  • spring.jpa.database — Setting the database type,
  • spring.datasource.url — Setting the URL for the database,
  • spring.datasource.username — Setting the database username,
  • spring.datasource.password — Setting the database password,
  • spring.jpa.show-sql — Enabling/Disabling (True/False) SQL query to be showing at logs,
  • spring.jpa.hibernate.ddl-auto — Setting the Hibernate to update the database schema based on the modifications in the domain model.

Use the username and password as per your MySQL installation

Model

Let’s create a data class called Player, and annotate with @Entity, this means that this class will be mapped in our database table, the @Table annotation is to set the custom table name (by default the name of the table is the same from the class), and the @Id is to define the ID.

@Entity
@Table(name = "tb_player")
data class Player(
@Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long,
val name: String,
val age: Int,
val nationality: String
)
view raw Player.kt hosted with ❤ by GitHub
Repository

Let’s now create our JPA repository, first create an interface called PlayerRepositoy, annotate with @Repository and extends JpaRepository by passing the model and the ID data type.

@Repository
interface PlayerRepository : JpaRepository<Player, Long>

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

Jobs

Service

Next, we will create a service class that we contain all methods for performs the operation in database using methods of the JPA repository:

  • getAll() function— will get all records from database,
  • getById() function— will get one record from the database based on the Id, or throw an exception if the Player with the specific Id doesn’t exist,
  • create() function— will create a record in the database,
  • remove() function— will remove a record from database based on the Id or throw an exception if the Player with the specific Id doesn’t exist,
  • update() function— will update a record from database based on the Id or throw an exception if the Player with the specific Id doesn’t exist.
@Service
class PlayerService(val repository: PlayerRepository) {
fun getAll(): List<Player> = repository.findAll()
fun getById(id: Long): Player = repository.findByIdOrNull(id) ?:
throw ResponseStatusException(HttpStatus.NOT_FOUND)
fun create(player: Player): Player = repository.save(player)
fun remove(id: Long) {
if (repository.existsById(id)) repository.deleteById(id)
else throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
fun update(id: Long, player: Player): Player {
return if (repository.existsById(id)) {
player.id = id
repository.save(player)
} else throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
}
Controller

Finally, we’ll create the REST controller that will provide endpoints for creating, manipulating, and deleting players. For that we need to annotate the controller with @RestController to say that this class is capable of handling requests and also annotate with @RequestMapping to define the path.

@RequestMapping("api/v1/players")
@RestController
class PlayerController(val service: PlayerService) {
@GetMapping
fun getAllPlayers() = service.getAll()
@GetMapping("/{id}")
fun getPlayer(@PathVariable id: Long) = service.getById(id)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun savePlayer(@RequestBody player: Player): Player = service.create(player)
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun deletePlayer(@PathVariable id: Long) = service.remove(id)
@PutMapping("/{id}")
fun updatePlayer(
@PathVariable id: Long, @RequestBody player: Player
) = service.update(id, player)
}

To handle incoming HTTP requests, we’re using spring provided annotations such as @GetMapping@PostMapping@DeleteMapping and @PutMapping.

Running the application

Now, we need to start our application by clicking on the run button in IDE or by terminal command using: mvn spring-boot:run

By default, spring boot application starts in port 8080

Testing the API

Now, it’s time to test our API.

POST— Creating a Player
#input
curl -X POST --location "http://localhost:8080/api/v1/players" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Leonel Messi\",
\"age\": 35,
\"nationality\": \"Argentina\"
}"
#output
{"id":1,"name":"Leonel Messi","age":35,"nationality":"Argentina"}%
view raw post.curl hosted with ❤ by GitHub
GET — Get all Players
#input
curl -X GET --location "http://localhost:8080/api/v1/players"
#output
[
{
"age": 35,
"id": 1,
"name": "Leonel Messi",
"nationality": "Argentina"
},
{
"age": 30,
"id": 2,
"name": "Neymar Jr",
"nationality": "Brazil"
}
]
view raw getAll.curl hosted with ❤ by GitHub
GET — Get Player by Id
#input
curl -X GET --location "http://localhost:8080/api/v1/players/1"
#output
{
"id":1,
"name":"Leonel Messi",
"age":35,
"nationality":"Argentina"
}
view raw byId.curl hosted with ❤ by GitHub
UPDATE — Update Player
#input
curl -X PUT --location "http://localhost:8080/api/v1/players/1" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Leo Messi\",
\"age\": 35,
\"nationality\": \"Argentina\"
}"
#output
{
"id":1,
"name":"Leo Messi",
"age":35,
"nationality":"Argentina"
}
view raw update.curl hosted with ❤ by GitHub
DELETE — Delete Player
curl -X DELETE --location "http://localhost:8080/api/v1/players/1"
view raw delete.curl hosted with ❤ by GitHub

You can check the full code in my GitHub repository here.

Thanks to you for reading this post! Please do 👏🏿 if you liked it and want more posts about Kotlin for server-side.

Thanks to Rosário Pereira Fernandes

This article was originally published on proandroiddev.com on July 15, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu