How do you define the same route in YAML versus the Java RouteLocatorBuilder DSL?
answer
- YAML -> RouteDefinition -> Route
- DSL: builder.routes().route(...).uri(...).build()
- shortcut string = FactoryName=args
- DSL composes with .and()/.or()/.negate()
- /actuator/gateway/refresh reloads YAML routes
basics
~10 sIn YAML you list routes under spring.cloud.gateway.routes with shortcut predicates like Path=/api/. In Java you declare a RouteLocator @Bean using RouteLocatorBuilder, chaining .route(id, r -> r.path("/api/").uri("...")). Both produce the same routes.
solid answer
~40 sBoth approaches build the same RouteLocator, just declaratively vs programmatically. In YAML each route under spring.cloud.gateway.routes has id, uri, a predicates list using shortcut syntax (Path=/api/**, Method=GET), and filters. The Java DSL exposes a RouteLocatorBuilder: you inject it into a @Bean method, call builder.routes(), then .route(id, predicateSpec -> ...) where the lambda chains typed predicate methods (.path(), .method(HttpMethod.GET)) combined with .and()/.or()/.negate(), and finishes with .uri(). YAML is best for static, ops-tunable config and can be reloaded via /actuator/gateway/refresh; the DSL gives you type safety, IDE support, loops, conditionals, and custom logic. You can use both together — they're merged. Under the hood YAML produces RouteDefinition objects that a RouteDefinitionRouteLocator turns into the same Route instances the DSL builds directly.
code
java · 15 lines@Configuration
public class GatewayConfig {
@Bean
RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
// equivalent to: Path=/orders/** + Method=GET,POST
.route("orders", r -> r
.path("/orders/**")
.and().method(HttpMethod.GET, HttpMethod.POST)
.filters(f -> f.stripPrefix(1))
.uri("lb://order-service"))
.build();
}
}go deeper
Can write a basic YAML route; may not know the DSL.
Should fluently translate between YAML and RouteLocatorBuilder and know shortcut arg binding.
Explains RouteDefinition->Route conversion and when DSL's programmability wins.
Weighs ops-tunable YAML + refresh vs compile-safe DSL as a config-governance decision.
There are two equivalent ways to declare routes; both ultimately produce `Route` objects consumed by the same `RoutePredicateHandlerMapping`. **1. YAML (declarative)** — under `spring.cloud.gateway.routes` (a list). Each element: ```yaml spring: cloud: gateway: routes: - id: orders uri: lb://order-service predicates: - Path=/orders/** - Method=GET,POST filters: - StripPrefix=1 ``` The strings like `Path=/orders/**` are **shortcut configuration**: the part before `=` names the predicate factory (`PathRoutePredicateFactory` → shortcut name `Path`), and the comma-separated values after `=` bind to that factory's args. Spring parses each entry into a **`RouteDefinition`** (id, uri, list of `PredicateDefinition`, list of `FilterDefinition`), and a **`RouteDefinitionRouteLocator`** converts those definitions into concrete `Route`s by looking up the matching factory beans. **2. Java/Kotlin DSL (programmatic)** — a `@Bean` returning `RouteLocator`, given a `RouteLocatorBuilder`: ```java @Bean RouteLocator routes(RouteLocatorBuilder b) { return b.routes() .route("orders", r -> r.path("/orders/**").and().method(HttpMethod.GET, HttpMethod.POST) .filters(f -> f.stripPrefix(1)) .uri("lb://order-service")) .build(); } ``` The lambda receives a `PredicateSpec`; each predicate method (`.path`, `.host`, `.method`, `.header`, `.query`, `.after`) returns a `BooleanSpec` on which you can call **`.and()`, `.or()`, `.negate()`** to compose further predicates, then `.filters(...)` and finally `.uri(...)`. **When to use which:** - YAML — static topology, editable by ops without recompiling, externally configurable per environment, and refreshable at runtime via the `/actuator/gateway/refresh` endpoint (re-reads `RouteDefinition`s). Great for the common case. - DSL — type safety and compile-time checks, IDE autocomplete, and the ability to build routes with loops/conditionals or custom `Predicate<ServerWebExchange>` logic that shortcut strings can't express. **They coexist:** routes from both sources are merged into the final route set (via a `CompositeRouteLocator` / `CachingRouteLocator`). A subtle gotcha: predicate ordering semantics differ slightly — in YAML predicates in the list are AND-ed automatically; in the DSL you must explicitly chain `.and()` between them (a bare second predicate method actually implies AND via the fluent API). Another gotcha: the shortcut arg order matters (`Header=X-Request-Id, \d+` is name then regex), and getting it wrong silently changes matching.
- How does the shortcut string 'Header=X-Request-Id, \d+' map to a predicate factory?The token before '=' names the factory (HeaderRoutePredicateFactory, shortcut 'Header'); the comma-separated args bind positionally to its config — first the header name 'X-Request-Id', then the value regex '\d+'.
- Can you refresh YAML routes without restarting the gateway?Yes — POST /actuator/gateway/refresh re-reads the RouteDefinitions and rebuilds the route cache. DSL routes defined in @Bean methods require a redeploy to change.
saying these in an interview costs you the question
- Claiming DSL and YAML routes can't be used together
- Thinking the DSL uses raw shortcut strings instead of typed methods
- Getting Header/Query shortcut arg order wrong (name vs regex)