kotlinadvanced

Spring WebFlux with Kotlin Coroutines

Build reactive Spring APIs using Kotlin coroutines: suspend functions, Flow, and coRouter DSL.

kotlin
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.delay
import org.springframework.web.bind.annotation.*
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType

data class Product(val id: String, val name: String, val price: Double)

interface ProductRepository {
    suspend fun findById(id: String): Product?
    suspend fun findAll(): List<Product>
    fun findAllAsFlow(): Flow<Product>
    suspend fun save(product: Product): Product
    suspend fun deleteById(id: String): Boolean
}

@RestController
@RequestMapping("/api/products")
class ProductController(private val repo: ProductRepository) {

    @GetMapping("/{id}")
    suspend fun findById(@PathVariable id: String): Product {
        return repo.findById(id)
            ?: throw NotFoundException("Product $id not found")
    }

    @GetMapping("", produces = [MediaType.APPLICATION_NDJSON_VALUE])
    fun findAll(): Flow<Product> = repo.findAllAsFlow()

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    suspend fun create(@RequestBody product: Product): Product {
        return repo.save(product)
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    suspend fun delete(@PathVariable id: String) {
        if (!repo.deleteById(id)) {
            throw NotFoundException("Product $id not found")
        }
    }

    @GetMapping("/stream", produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
    fun priceStream(): Flow<Map<String, Any>> = flow {
        while (true) {
            val products = repo.findAll()
            products.forEach { product ->
                emit(mapOf(
                    "id" to product.id,
                    "name" to product.name,
                    "price" to product.price * (0.95 + Math.random() * 0.1)
                ))
            }
            delay(1000)
        }
    }
}

@ResponseStatus(HttpStatus.NOT_FOUND)
class NotFoundException(message: String) : RuntimeException(message)

Sponsored

Railway

Use Cases

  • Non-blocking REST APIs with Kotlin coroutines
  • Server-Sent Events for real-time data
  • Reactive microservices with Spring WebFlux

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.