iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Bootcamp

Kotlin + Spring Boot one-day bootcamp: scaffold, controllers, JPA, coroutines, tests, deploy.

Kotlin — Spring Boot bootcamp

EXAMPLE
# ===== 0-30 min: scaffold =====
# Spring Initializr (https://start.spring.io):
# Language: Kotlin, Build: Gradle Kotlin, JVM 21, Spring Web + Spring Data JPA + Postgres + Validation

curl https://start.spring.io/starter.zip -o demo.zip \
  -d type=gradle-project-kotlin \
  -d language=kotlin \
  -d bootVersion=3.3.0 \
  -d groupId=com.example \
  -d artifactId=shop \
  -d dependencies=web,data-jpa,postgresql,validation,actuator

unzip demo.zip && cd shop

# ===== 30-60 min: main + controller =====
# src/main/kotlin/com/example/shop/ShopApplication.kt
@SpringBootApplication
class ShopApplication

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

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

    @GetMapping("/{id}")
    fun get(@PathVariable id: Long): UserDto =
        service.findById(id) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun create(@Valid @RequestBody dto: CreateUserDto): UserDto = service.create(dto)
}

# ===== 60-120 min: entity + repository =====
@Entity
@Table(name = "users")
class User(
    @Column(unique = true, nullable = false) var email: String,
    @Column(nullable = false) var name: String,
) {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null
    @CreationTimestamp var createdAt: Instant? = null
}

interface UserRepository : JpaRepository<User, Long> {
    fun findByEmail(email: String): User?
}

# DTOs:
data class CreateUserDto(@field:Email val email: String, @field:NotBlank val name: String)
data class UserDto(val id: Long, val email: String, val name: String)
fun User.toDto() = UserDto(id!!, email, name)

# ===== 120-180 min: service =====
@Service
class UserService(private val repo: UserRepository) {

    fun findById(id: Long): UserDto? = repo.findById(id).map { it.toDto() }.orElse(null)

    @Transactional
    fun create(dto: CreateUserDto): UserDto {
        val user = User(email = dto.email, name = dto.name)
        return repo.save(user).toDto()
    }
}

# ===== 180-240 min: configuration =====
# src/main/resources/application.yml
spring:
  application:
    name: shop
  datasource:
    url: jdbc:postgresql://localhost:5432/shop
    username: dev
    password: dev
  jpa:
    hibernate:
      ddl-auto: validate     # never 'update' in production
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus

# ===== 240-300 min: coroutines =====
# Spring 6 supports suspend functions in controllers:

@RestController
class AsyncController(private val client: WebClient) {

    @GetMapping("/api/external")
    suspend fun callExternal(): String {
        return client.get().uri("/data").retrieve().awaitBody()
    }
}

# Add reactor-kotlin + kotlinx-coroutines-reactor dependencies.

# ===== 300-360 min: tests =====
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest @Autowired constructor(val mvc: MockMvc) {

    @Test
    fun `creates user`() {
        mvc.perform(post("/api/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content("""{"email":"a@x.io","name":"Alex"}"""))
            .andExpect(status().isCreated)
            .andExpect(jsonPath("\$.email").value("a@x.io"))
    }
}

# ===== 360-420 min: build + deploy =====
./gradlew bootJar
java -jar build/libs/shop-0.0.1-SNAPSHOT.jar

# Docker:
./gradlew bootBuildImage    # Spring's built-in image builder
docker run -p 8080:8080 shop:latest

# ===== Patterns to internalise =====
# - val by default; data class for DTOs
# - Constructor injection
# - @Transactional on writes
# - JPA migrations via Flyway / Liquibase
# - Actuator + Prometheus + Grafana for metrics

# ===== Pitfalls =====
# - ddl-auto: update in production
# - Mutable JPA entities + 'data class' (avoid; use plain class)
# - !! everywhere -> defeats null safety
# - Blocking calls inside suspend functions without Dispatchers.IO

Why it matters

A one-day Kotlin + Spring Boot bootcamp: scaffold, controller, entity, repository, service, suspend, tests, deploy. Same shape as Java Spring Boot but with Kotlin reflexes (val, data class, null-safe types, coroutines). Production-ready in a day.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// 30-day Kotlin bootcamp in the lesson body.
Try it Yourself »

Discussion

Loading…