skip to content

Reactive MongoDB

The reactive Mongo repositories and template return Mono and Flux over the reactive driver, and support tailable cursors and change streams for live updates. A natural pairing question with WebFlux, since Mongo is one of the few stores with a genuinely reactive driver.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

6

What is ReactiveMongoRepository and what do its query methods return?

level: juniorimportance: must knowfreq 70%

answer

  1. ReactiveMongoRepository = reactive MongoRepository
  2. Mono = 0..1, Flux = 0..N
  3. Cold publisher: runs on subscribe
  4. No .block() on event loop
  5. @EnableReactiveMongoRepositories + reactive driver

basics

~10 s

It is Spring Data's reactive interface for MongoDB. Query methods return Mono<T> for zero-or-one result and Flux<T> for many. Nothing runs until you subscribe.

solid answer

~40 s

ReactiveMongoRepository<T, ID> is the reactive counterpart of MongoRepository, built on Project Reactor and the reactive MongoDB driver. Derived and @Query methods return Mono<T> (0..1) or Flux<T> (0..N) instead of blocking values. These are lazy publishers: no query is sent to MongoDB until something subscribes, typically WebFlux subscribes when a controller returns the publisher. You enable it with @EnableReactiveMongoRepositories plus a ReactiveMongoTemplate/ReactiveMongoDatabaseFactory. Because the whole path is non-blocking, a small event-loop thread pool serves many concurrent requests without parking a thread per query. You never call .block() inside reactive code; you compose with map, flatMap, filter and let the framework drive the subscription.

code

java · 19 lines
java
public interface UserRepository extends ReactiveMongoRepository<User, String> {

    Flux<User> findByStatus(String status);   // 0..N

    Mono<User> findByEmail(String email);      // 0..1
}

@RestController
@RequiredArgsConstructor
class UserController {
    private final UserRepository repo;

    @GetMapping("/users/{id}")
    Mono<User> byId(@PathVariable String id) {
        // WebFlux subscribes to this Mono for us; no .block()
        return repo.findById(id)
                   .switchIfEmpty(Mono.error(new NoSuchElementException(id)));
    }
}

go deeper

for a junior

Know Mono vs Flux and that methods return publishers, not values.

for a middle

Explain cold/lazy subscription and who subscribes in WebFlux.

for a senior

Discuss when reactive is worth it vs blocking MongoRepository and thread-model implications.

for a principal

Weigh end-to-end reactivity, transaction limits, and operational complexity across the whole stack.

**Reactive MongoDB** lets you query MongoDB without blocking a thread while the database works. It rests on two things: **Project Reactor** (the reactive-streams library that provides the `Mono` and `Flux` types) and the **MongoDB Reactive Streams Java Driver** (a non-blocking driver that pushes results as they arrive over the wire). **`ReactiveMongoRepository<T, ID>`** is the reactive sibling of the blocking `MongoRepository`. It extends `ReactiveCrudRepository` and `ReactiveSortingRepository`. You declare an interface and Spring Data generates the implementation at startup by parsing method names (derived queries) or reading `@Query` annotations. - **`Mono<T>`** is a publisher of **zero or one** element (then completion or error). Used for `findById`, `save`, `count`, `existsById`, `deleteById`. - **`Flux<T>`** is a publisher of **zero to many** elements. Used for `findAll` and any derived finder that can match multiple documents. **Laziness / cold publishers.** A returned `Mono`/`Flux` is a *recipe*, not a running query. The actual `find` command is only sent to MongoDB when something **subscribes**. In a Spring WebFlux app the framework subscribes for you when a `@RestController` method returns the publisher; in tests you subscribe via `StepVerifier` or (rarely) `.block()`. Forgetting to return/subscribe means the query silently never runs. **Enabling it.** You need `spring-boot-starter-data-mongodb-reactive`, which wires a `ReactiveMongoDatabaseFactory`, a `ReactiveMongoTemplate`, and turns on `@EnableReactiveMongoRepositories` (auto-configured by Boot). The connection uses `mongodb://` URIs the same as the blocking driver. **Composition, not blocking.** Inside reactive code you transform with operators: `map` (sync 1:1 transform), `flatMap` (async, returns another publisher, e.g. a dependent DB call), `filter`, `switchIfEmpty` (fallback when a `Mono` is empty). Calling `.block()` on the event loop defeats the purpose and can deadlock the small Reactor/Netty thread pool. **When to use.** Choose the reactive stack when you have an end-to-end non-blocking pipeline (WebFlux + reactive driver) and high concurrency with I/O-bound work. If the rest of your app is blocking (JDBC, blocking web), the reactive Mongo layer gives little benefit and adds complexity, so plain `MongoRepository` is usually better. **Gotchas.** (1) You cannot mix a blocking `.block()` call on Reactor's parallel/event-loop threads. (2) `Flux`/`Mono` are single-shot per subscription but re-subscribable; re-subscribing re-runs the query. (3) Reactive repositories do **not** support the same declarative `@Transactional` ergonomics as blocking ones; multi-document transactions use `TransactionalOperator` / reactive transaction manager against a replica set.

  • Why must you avoid calling .block() inside a WebFlux handler?
    WebFlux runs on a small event-loop thread pool. .block() parks that thread waiting for the result, so under load you exhaust the loop threads and can deadlock, destroying the non-blocking benefit.
  • What happens if a controller method builds a Flux but never returns or subscribes to it?
    Nothing. The publisher is cold, so with no subscriber the query is never sent to MongoDB and no data flows.

saying these in an interview costs you the question

  • Saying the query runs as soon as the repository method is called (it is lazy until subscribe)
  • Claiming Mono can emit many elements (that is Flux)
  • Recommending .block() to 'get the value' inside reactive code

context

open as a page

How is backpressure handled on a result Flux from a reactive MongoDB query?

level: seniorimportance: must knowfreq 45%

basics

~20 s

The reactive driver honours reactive-streams demand: the subscriber requests N items, and the driver fetches from MongoDB in batches to match, pausing when demand is zero. So a slow consumer naturally throttles the query instead of buffering everything.

open as a page

What does @Tailable do on a reactive repository method, and what are its requirements?

level: middleimportance: should knowfreq 45%

basics

~10 s

@Tailable makes a Flux query use a tailable cursor: it stays open and keeps emitting new matching documents as they are inserted, like tail -f. It requires a capped collection and must return Flux.

open as a page

When would you use ReactiveMongoTemplate instead of a ReactiveMongoRepository?

level: middleimportance: should knowfreq 50%

basics

~10 s

Use the repository for simple CRUD and derived queries. Drop to ReactiveMongoTemplate when you need dynamic queries, aggregations, partial updates, upserts, or fine control the repository abstraction cannot express, still returning Mono/Flux.

open as a page

How do reactive change streams work in Spring Data MongoDB, and how do they differ from @Tailable?

level: seniorimportance: should knowfreq 40%

basics

~20 s

changeStream on ReactiveMongoTemplate opens a Flux of change events (insert, update, delete, replace) on any collection, backed by the replica-set oplog. Unlike @Tailable it needs no capped collection, sees all operation types, and supports resume tokens.

open as a page

You run a reactive change-stream listener as a long-lived Flux in production. What failure modes and design concerns must you address?

level: principalimportance: should knowfreq 25%

basics

~20 s

Handle stream errors with resubscription, persist and resume from tokens for gap-free at-least-once delivery, make handlers idempotent, keep the oplog window larger than max consumer lag, bound backpressure, and coordinate consumers so events are processed once across instances.

open as a page