kotlinintermediate

Spring Boot REST API in Kotlin

Build a REST API with Spring Boot and Kotlin: controllers, services, error handling, and validation.

kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.*
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Service
import jakarta.validation.Valid
import jakarta.validation.constraints.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong

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

// DTOs
data class CreateUserRequest(
    @field:NotBlank val name: String,
    @field:Email val email: String,
    @field:Min(0) @field:Max(150) val age: Int
)

data class UserResponse(
    val id: Long,
    val name: String,
    val email: String,
    val age: Int
)

data class ErrorResponse(
    val status: Int,
    val message: String,
    val timestamp: Long = System.currentTimeMillis()
)

// Domain
data class User(val id: Long, val name: String, val email: String, val age: Int)

// Service
@Service
class UserService {
    private val users = ConcurrentHashMap<Long, User>()
    private val idGen = AtomicLong(0)

    fun findAll(): List<User> = users.values.toList()

    fun findById(id: Long): User =
        users[id] ?: throw NotFoundException("User $id not found")

    fun create(request: CreateUserRequest): User {
        val user = User(idGen.incrementAndGet(), request.name, request.email, request.age)
        users[user.id] = user
        return user
    }

    fun delete(id: Long) {
        users.remove(id) ?: throw NotFoundException("User $id not found")
    }
}

class NotFoundException(message: String) : RuntimeException(message)

// Controller
@RestController
@RequestMapping("/api/users")
class UserController(private val service: UserService) {

    @GetMapping
    fun list() = service.findAll().map { it.toResponse() }

    @GetMapping("/{id}")
    fun get(@PathVariable id: Long) = service.findById(id).toResponse()

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun create(@Valid @RequestBody request: CreateUserRequest) =
        service.create(request).toResponse()

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    fun delete(@PathVariable id: Long) = service.delete(id)
}

// Extension function for mapping
fun User.toResponse() = UserResponse(id, name, email, age)

// Global error handler
@RestControllerAdvice
class GlobalExceptionHandler {
    @ExceptionHandler(NotFoundException::class)
    fun handleNotFound(ex: NotFoundException) =
        ResponseEntity.status(404).body(ErrorResponse(404, ex.message ?: "Not found"))

    @ExceptionHandler(Exception::class)
    fun handleGeneral(ex: Exception) =
        ResponseEntity.status(500).body(ErrorResponse(500, ex.message ?: "Internal error"))
}

Sponsored

Railway

Use Cases

  • RESTful API development with Kotlin
  • Spring Boot microservices
  • CRUD operations with validation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.