Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Route structure

A YAML route file defines one or more routes. Each route has an identifier, a source endpoint, and an ordered list of processing steps.

Minimal route

The smallest useful route reads from a source and writes to a destination.

routes:
  - id: "hello-timer"
    from: "timer:tick?period=2000&repeatCount=3"
    steps:
      - log: "Hello from config-loaded route!"
      - to: "log:info"

The file opens with a routes list. Each list entry is one route object. The route above, hello-timer, reads from a timer endpoint, logs a greeting, and forwards the exchange to the log:info endpoint.

Route fields

Each route object accepts these top-level fields. Only id and from are required. The error_handler and circuit_breaker objects are documented below. The security_policy object is documented in Authorization.

FieldTypeRequiredDefaultDescription
idstringyesUnique route identifier
fromstringyesSource endpoint URI
stepslistno[]Ordered step verbs
auto_startupboolnotrueStart the route when the context starts
startup_orderintegerno1000Ascending start order; shutdown reverses it
sequentialboolnofalseProcess exchanges one at a time
concurrentintegernoMaximum concurrent exchanges
error_handlerobjectnoPer-route error handler
circuit_breakerobjectnoRoute-level circuit breaker with optional fallback sub-pipeline
security_policyobjectnoRoute-level authorization
on_completestringnoProducer URI for the success hook
on_failurestringnoProducer URI for the failure hook

auto_startup: false registers the route but does not start its consumer. Start it later through the route controller or control bus. concurrent caps how many exchanges the pipeline processes in parallel; omit it to let the runtime decide. on_complete and on_failure fire when an exchange exits the pipeline, on success or on error respectively.

Error handling

Set error_handler to retry failed exchanges and send them to a dead letter channel when retries run out. The handler holds a redelivery policy and optional per-exception clauses. The full field set lives on the step verbs reference.

Circuit breaker

Set circuit_breaker to protect a route from a failing downstream service. The breaker opens after failure_threshold consecutive failures. While open, it rejects exchanges for open_duration_ms. See Circuit breaker for the breaker states.

The optional fallback list holds a sub-pipeline. The breaker runs the sub-pipeline instead of rejecting the exchange while the circuit is open. An absent or empty fallback keeps the existing behavior: the breaker returns CircuitOpen.

    circuit_breaker:
      failure_threshold: 1
      open_duration_ms: 60000
      fallback:
        - cache_peek_stale:
            key: "user-profile-42"

The fallback runs on routes with and without an error_handler. A fallback step that stops cleanly (for example, a cache_peek_stale MISS with the default on_miss: stop) surfaces Ok(exchange) with the exchange state intact. No CircuitOpen escapes.

A failing fallback step follows the route's error handling. A route with an error_handler routes the failure through the handler. A route without one surfaces the raw error to the caller.

The Cache page shows the stale-on-error composition with cache_peek_stale.

Half-open fallback asymmetry

During the half-open probe-in-flight window, the fallback behavior differs by route shape. A route with an error_handler compiles the breaker into a CircuitBreakerGate. The gate serves the fallback to concurrent callers while the probe runs. A route without an error_handler compiles the breaker into a Tower CircuitBreakerService. That service rejects concurrent callers with CircuitOpen while the probe runs, even when a fallback is configured.

Both behaviors are sound. The gate keeps a single probe in flight and serves stale fallback data. The service keeps a single probe in flight and rejects. The asymmetry is intentional. See Circuit breaker for the breaker states.

Security policy

Set security_policy to authorize exchanges before the steps run. The object takes one of five forms: roles, scopes, ref, wasm, or permission. The full policy model lives in Authorization.

The optional credential_sources list names where the route reads its credential. When absent, the default is [authorization_header]: the route reads the Authorization header only. Each entry names one source:

FormMeaning
authorization_headerBearer token in the Authorization header
query_param: { param: <name> }Token in a query parameter
cookie: { name: <name> }Token in a cookie
header: { name: <name> }API key in a named custom header
routes:
  - id: tile-service
    from: "http://0.0.0.0:8090/tiles"
    security_policy:
      roles: ["tile-user"]
      credential_sources:
        - cookie: { name: session }
        - query_param: { param: token }
        - header: { name: X-Api-Key }
        - authorization_header
    steps:
      - set_header:
          key: CamelHttpResponseCode
          value: 200
      - set_header:
          key: Content-Type
          value: "application/json"
      - set_body: '{"layer":"streets","status":"authenticated"}'

Extraction runs in the declared order. The first source that supplies a credential wins. credential_sources is valid only with the roles or scopes form. Load-time validation rejects malformed credential_sources entries: an empty list, an empty parameter or cookie name, and a header name that is not a valid RFC 9110 token. See Authorization for the extraction semantics and ADR-0059.

The optional provider string names the configured authenticator for the route. The name must match one of the configured providers: keycloak, oidc, or native. When more than one provider is configured, provider is required. A route without it fails to load, and the error names the available providers. An unknown provider name also fails the load. Like credential_sources, provider is valid only with the roles or scopes form.

REST block policy

A rest: block accepts the same security_policy object, declared once on the block. Lowering copies it onto every route the block produces, so every operation enforces the same policy before its handler runs. The object takes the same five forms and passes the same load-time validation as the route-level key, including credential_sources, provider, and audiences.

rest:
  - host: 0.0.0.0
    port: 9090
    path: /api/users
    security_policy:
      roles: ["user"]
      provider: "native-demo"
    operations:
      - method: GET
        operation_id: listUsers
        to: direct:listUsers
        produces: application/json
      - method: POST
        operation_id: createUser
        consumes: application/json
        produces: application/json
        success_status: 201
        to: direct:createUser
      - method: PUT
        path: /{id}
        operation_id: updateUser
        consumes: application/json
        produces: application/json
        to: direct:updateUser
      - method: DELETE
        path: /{id}
        operation_id: deleteUser
        to: direct:deleteUser
        success_status: 204

The source lives at examples/rest-crud/routes/secured.yaml. Run the runnable variant with cargo run -p rest-crud --bin secured: a request without a credential gets 401 before any handler runs, and a token whose principal holds the required role is granted. A block without security_policy lowers to public routes.

List-form variant

The hot-reload subsystem consumes a flatter form. When a file holds only routes and omits the top-level routes wrapper, the parser reads the file as a bare list of route objects:

- from: timer:hot-reload?period=1000
  route_id: hot-reload-route
  steps:
    - to: log:info?showHeaders=true

Each entry uses route_id instead of id and carries from and steps. This list form maps to the canonical route contract the runtime exchanges over the control bus, not the full authoring model.

Next

Reference: DSL crate