Everything You Need to Know about asynchronous programming along with Kotlin + Spring WebFlux



This content originally appeared on DEV Community and was authored by Thales

General Introduction

What is Reactive Programming?

Wikipedia: Reactive Programming

Reactive programming is a declarative programming paradigm focused on data streams and the propagation of change. It allows working with both static (e.g. arrays) and dynamic (e.g. event emitters) data flows, automatically handling updates via the Observer pattern.

In essence, reactive programming is about data flow, asynchronous communication, and automatic propagation of changes.

In Spring, the key difference between traditional Spring MVC and WebFlux is synchronous vs asynchronous processing. Instead of blocking to wait for a response, reactive code allows you to invoke a method and keep doing other work while waiting.

Spring uses Project Reactor internally, which implements the Reactive Streams Specification.

Reactive Streams Specification

Reactive Streams GitHub README

The Reactive Streams API consists of the following components:

  • Publisher
  • Subscriber
  • Subscription
  • Processor

Publisher

interface Publisher<T> {
    fun subscribe(subscriber: Subscriber<in T>)
}

A Publisher emits elements — but only on demand, meaning a Subscriber must request items.

Subscriber

interface Subscriber<T> {
    fun onSubscribe(subscription: Subscription)
    fun onNext(t: T)
    fun onError(t: Throwable)
    fun onComplete()
}

Handles emitted data, errors, and stream completion.

Subscription

interface Subscription {
    fun request(n: Long)
    fun cancel()
}

Controls data flow (backpressure). A Subscriber uses this to request elements.

Processor

interface Processor<T, R> : Subscriber<T>, Publisher<R>

Transforms a stream — acts as both subscriber and publisher.

⚠ In practice, we don’t implement these interfaces manually—that would be a lot of work. Instead, we use libraries that already implement them. One such library is Project Reactor, which implements the Reactive Streams Specification and is part of the Spring WebFlux module. It uses classes like Flux and Mono, which act as publishers or subscribers.

Project Reactor

Project Reactor Documentation

Why use Reactor?

  • Avoids thread blocking during I/O.
  • Non-blocking by default → better resource usage.
  • Ideal for high concurrency systems (e.g., Black Friday sales).

Reactor is event-loop based: requests go into the loop and get picked up asynchronously.

Spring WebFlux

Spring WebFlux Docs

Spring WebFlux is the reactive web stack introduced in Spring 5.0.

  • Fully non-blocking
  • Supports backpressure
  • Can run on Netty or Servlet containers (3.1+)

WebFlux works with two main types from Reactor:

Type Description
Mono Emits 0 or 1 item
Flux Emits 0 to N items

Operators: Mono & Flux

Mono

Mono.just("data")

Emits a single item and completes.

Flux

Flux.just("a", "b", "c")

Emits multiple items, one after another.

Timeline representation

  • Black line: stream completed
  • Red X: error occurred

To find methods that best suit your needs, follow the guide below:

📘 Reactor Operator Guide

Common Methods in Kotlin

.map()

Transforms value inside Mono/Flux.

Mono.just("5")
    .map { it.toInt() }

.flatMap()

Transforms and flattens nested streams.

Mono.just("5")
    .flatMap { id -> getUserById(id) }

Hands-On: Project

  • Build reactive pipelines
  • Chain operators
  • Handle exceptions properly

Error Handling in Streams

In reactive streams, errors are terminal unless explicitly handled.

Common patterns:

.onErrorResume { Mono.just(fallbackValue) }
.onErrorReturn(defaultValue)

Uncaught exceptions may throw UnsupportedOperatorException or other runtime errors.

Functional Endpoints

Prefer separating routing from logic using functional endpoints.

Router

val route = coRouter {
    accept(APPLICATION_JSON).nest {
        GET("/person/{id}", handler::getPerson)
        GET("/person", handler::listPeople)
    }
    POST("/person", handler::createPerson)
}

Handler

class PersonHandler(private val repository: PersonRepository) {
    suspend fun listPeople(request: ServerRequest): ServerResponse { ... }
    suspend fun createPerson(request: ServerRequest): ServerResponse { ... }
    suspend fun getPerson(request: ServerRequest): ServerResponse { ... }
}

The Four Pillars of Reactive Programming

Pillar Meaning
Responsive Reacts quickly to user actions
Resilient Recovers from failures
Elastic Scales with demand (multicore, cloud-native, etc.)
Message-driven Uses async communication and message passing

Supporting Material & References


This content originally appeared on DEV Community and was authored by Thales