A hands-on workshop project that teaches the core concepts of the Vert.x toolkit through a small reactive service that ingests and queries sensor readings, stored in a MongoDB time-series collection. The workshop is meant to be accompanied by slides and live coding/commentary.
It covers:
- verticles
- the reactive (RxJava 3) Vert.x API
- the web router
- an asynchronous MongoDB client (including creating a time-series collection and running an aggregation)
- streaming responses
- integration testing with Vert.x JUnit 5 and Testcontainers.
Note: historically this workshop was based on a different domain and legacy versions of Vert.x and RxJava; it was modernized as of mid-2026.
- Java 21
- Vert.x 5.1 (core, web, web-client, mongo-client, rx-java3)
- RxJava 3
- MongoDB 7
- JUnit 5 +
vertx-junit5+ Testcontainers for tests
Ingest (record) and the streamed time-range query (history) are implemented for you as a
reference. Your job is to implement the downsampling endpoint:
- Per-minute average —
GET /sensor/:sensorId/averageSensorReadingRepository.averagePerMinute— throwsUnsupportedOperationExceptionSensorReadingVerticle.averagePerMinute— throwsUnsupportedOperationException(the route is already wired withrespond(...); collect the buckets and return them as aMaybe)
Both TODO comments walk you through the steps. One integration test is @Disabled for this
endpoint — remove the annotation and make it pass:
SensorReadingVerticleTest.average_shouldReturnOneMinuteBuckets_whenValidSensorIdAndWindowPassed.
Stuck, or want to check your answer? The full reference implementation is on the
solutionbranch:git checkout solution(orgit diff main..solution).
mvn test— run the tests (spins up MongoDB via Testcontainers; requires Docker)docker compose up --build— build and run the whole stack (service + MongoDB)
The fat jar's entry point is Vert.x's own io.vertx.launcher.application.VertxApplication; it boots
the Main-Verticle declared in the jar manifest. Configuration is resolved in two layers: the
verticle first reads file defaults from $CONF_PATH (default conf/config.json), then
deep-merges its deployment config on top, so any key from --conf on the command line or a
test's DeploymentOptions wins.
- record a reading —
curl http://localhost:8080/reading -i -H "Content-Type: application/json" -X POST -d '{"sensorId":"kitchen-1","temperature":21.4,"ts":"2026-07-03T10:00:00Z"}' - history (streamed) —
curl "http://localhost:8080/sensor/kitchen-1/readings?from=2026-07-03T00:00:00Z&to=2026-07-04T00:00:00Z" -i - per-minute average (exercise) —
curl "http://localhost:8080/sensor/kitchen-1/average?from=2026-07-03T00:00:00Z&to=2026-07-04T00:00:00Z" -i