Mastering Spring Boot RouterFunction: Build Clean Reactive Routes
This guide explains how to use Spring Boot's RouterFunction API to create, combine, and nest reactive routes with RequestPredicates, demonstrating practical code examples for GET, POST, and complex route compositions in a Spring WebFlux application.
Overview
Router functions map incoming requests to a HandlerFunction . Instead of writing them manually, you typically use the static methods in the RouterFunctions class. RouterFunctions.route() provides a fluent builder, while RouterFunctions.route(RequestPredicate, HandlerFunction) offers a direct creation method.
The builder style route() is recommended because it supplies convenient shortcuts for common mappings, such as GET(String, HandlerFunction) and POST(String, HandlerFunction) .
Predicates
You can implement custom RequestPredicate s, but the RequestPredicates class already supplies common implementations based on path, HTTP method, content type, etc.
<code>RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("Hello World"))
.build();</code>Predicates can be combined using RequestPredicate.and(other) (both must match) or RequestPredicate.or(other) (either may match). Many predicates are composites; for example, RequestPredicates.GET(String) combines method(HttpMethod) and path(String) .
Routes
Router functions are evaluated sequentially: if the first route does not match, the next is tried. Therefore, more specific routes should be declared before generic ones, especially when registering the router as a Spring bean.
Multiple router functions can be merged using:
add(RouterFunction) on the builder
RouterFunction.and(RouterFunction)
RouterFunction.andRoute(RequestPredicate, HandlerFunction) (shortcut for nested routes)
<code>import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
PersonRepository repository = ...;
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> otherRoute = ...;
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) // 1
.GET("/person", accept(APPLICATION_JSON), handler::listPeople) // 2
.POST("/person", handler::createPerson) // 3
.add(otherRoute) // 4
.build();</code>GET /person/{id} with JSON Accept header routes to PersonHandler.getPerson .
GET /person with JSON Accept header routes to PersonHandler.listPeople .
POST /person routes to PersonHandler.createPerson .
otherRoute is an externally defined router function added to the composition.
Nested Routes
When several routes share a common predicate (e.g., a base path), you can group them using path() :
<code>RouterFunction<ServerResponse> route = route()
.path("/person", builder -> builder
.GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET(accept(APPLICATION_JSON), handler::listPeople)
.POST(handler::createPerson))
.build();</code>Further nesting is possible with nest() to combine additional predicates such as an Accept header:
<code>RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
.nest(accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople))
.POST(handler::createPerson))
.build();</code>Conclusion
The article covered the practical use of RouterFunction in Spring Boot, including basic route creation, predicate composition, route combination techniques, and nested routing for shared predicates.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.