Rule Cascade
ReferenceExample READMEs

Node.js example (node:http)

The payments API of backend-spring-boot, on plain node:http with the TypeScript runtime and no other dependency. It enforces payments-transfer.ruleset.yaml from its compiled bundle.

The payments API of backend-spring-boot, on plain node:http with the TypeScript runtime and no other dependency. It enforces payments-transfer.ruleset.yaml (examples/contracts/payments-transfer.ruleset.yaml) from its compiled bundle.

FileWhat it shows
src/app.mjsThe four steps on every operation: evaluate, refuse on deny with 422 problem details, persist, run commands once per idempotency key. Read rules that hide fields. The client manifest with an ETag
src/main.mjsLoad the bundle once at startup; a bundle that cannot be read stops the process
test/app.test.mjsThe behaviour above through real HTTP requests (node:test)

Run it

From the repository root:

npm ci && npm run build                          # builds the runtime this example imports
npm test -w rule-cascade-example-backend-node
npm start -w rule-cascade-example-backend-node   # listens on :8080
# Allowed, with a non-blocking warning about the missing memo
curl -s localhost:8080/transfers -H 'Content-Type: application/json' -d '{
  "transfer": {"type": "domestic", "amount": 120.50, "currency": "USD",
               "beneficiary": {"name": "Jo Lee", "country": "US"}}}'

# Denied: 422 application/problem+json with the evaluation attached
curl -s localhost:8080/transfers -H 'Content-Type: application/json' -d '{
  "transfer": {"type": "international", "amount": 500, "currency": "USD", "memo": "gift",
               "beneficiary": {"name": "X", "country": "KP", "swiftCode": "ABCDKPPY"}}}'

# What a browser fetches; send the ETag back as If-None-Match to get 304
curl -si localhost:8080/rulesets/acme.payments.transfer/manifest | head -5

RULES_BUNDLE points at another bundle and PORT changes the port.

Bundle or source

The service reads conformance/bundles/acme.payments.transfer.bundle.json, the compiled form of the example contract, so it needs no YAML parser and repeats no load-time check. Compile your own with any runtime's command, for example rule-cascade compile examples/contracts/payments-transfer.ruleset.yaml -o transfer.bundle.json. To compile from YAML at startup instead, use loadRuleSet with createSchemaValidator() as shown in packages/typescript.

A bundle contains server-only rules. Serve browsers rules.manifest('client'), never the bundle.

The actor

The actor comes from the X-Actor-Id and X-Actor-Roles headers to keep the example short. A real service takes it from its authentication layer and never trusts a role a caller claims for itself.

On this page