Your first rule in an app
A step-by-step walkthrough for beginners - write one business rule, test it, and enforce it in an Express, FastAPI, Spring Boot or Go service with one line, then change the rule without touching the code.
This page takes you from nothing to an API that refuses a bad order, in about fifteen minutes. You need a terminal and one of Node.js 20+, Python 3.10+, Java 17+ or Go 1.22+. No Rule Cascade knowledge is assumed.
The idea in one sentence: you write the business rule once, as data (YAML), and every part of your system asks the same rule instead of re-implementing it in code. Here the rule is "an order has at most 10 items".
Install the rcas command
curl -fsSL https://rulescascade.com/install.sh | sh # macOS and Linux
irm https://rulescascade.com/install.ps1 | iex # Windows PowerShell
npx -y @rules-cascade/cli version # or run it through npm, nothing to installrcas checks, tests and compiles rules. Your application does not need it at run time.
Create a project with a first rule
mkdir shop && cd shop
rcas init --name shoprcas init /home/you/shop (shop, ids shop.*)
created rcas.yaml
created rules/example.ruleset.yaml
created rules/order.schema.json
created rules/LOADING.mdOpen rules/example.ruleset.yaml. The part that matters (shortened):
params:
maxQuantity: { type: integer, default: 10 } # the limit, as a parameter
rules:
- id: order.quantity.max # a stable name for the rule
target: { entity: Order, field: /quantity }
operations: [create, update]
when: { op: exists, args: [{ var: data.quantity }] } # only when there is a quantity
assert: { op: lte, args: [{ var: data.quantity }, { var: params.maxQuantity }] } # quantity <= 10
severity: error # error blocks; warning only warns
finding: { code: EXAMPLE-ORD-001, message: order.quantityTooHigh, args: { max: { var: params.maxQuantity } } }
messages:
en:
order.quantityTooHigh: "You can order at most {max} items."Read it as: when an Order is created or updated and it has a quantity, the quantity must be at most
maxQuantity; otherwise block it with code EXAMPLE-ORD-001. The file also has a second rule (a
warning for large orders without a note) and golden tests at the end: example orders with the
answer the rules must give.
Check the rules
rcas checkshop.example@0.1.0 sha256:21eff22a5c25... 2 rules (2 client-safe), 1 params
3 golden tests, 0 failedcheck validates the file and runs the golden tests. Change default: 10 to default: 2 and run
it again: the test "three items are allowed" fails. That is the point of golden tests: a rule
change that breaks an agreed example never goes unnoticed. Put it back to 10.
Compile
rcas compile --allshop.example@0.1.0 sha256:21eff22a... -> shop.example.bundle.json, shop.example.client.manifest.jsonbuild/rules/shop.example.bundle.json is what your application loads: the rules, checked and
compiled, with a checksum. You can try it without any application:
echo '{"entity":"Order","operation":"create","data":{"quantity":11}}' | rcas evaluate --bundle build/rules/shop.example.bundle.json -The answer has "decision": "deny" and one finding, EXAMPLE-ORD-001, "You can order at most 10
items."
Enforce the rule in your application
Version
enforce, the middleware, the decorator and the 422 helpers are in the packages from
1.0.0-alpha.7.Pick your stack. In each one the rules are loaded once, at start-up, and one line (a middleware, a decorator, an annotation) enforces them on the endpoint: a denied order never reaches your code, and the caller gets a 422 answer that says why.
npm install express @rules-cascade/coreimport { readFileSync } from 'node:fs';
import express from 'express';
import { RuleSet } from '@rules-cascade/core';
import { rulesMiddleware, rulesErrorHandler } from '@rules-cascade/core/http';
// Once, at start-up: a bundle that cannot be read stops the server.
const rules = RuleSet.fromBundle(JSON.parse(readFileSync('build/rules/shop.example.bundle.json', 'utf8')));
const app = express();
app.use(express.json());
app.post('/orders', rulesMiddleware(rules, { entity: 'Order', operation: 'create' }), (req, res) => {
// Only allowed orders get here. Warnings (non-blocking findings) come with the evaluation.
const warnings = res.locals.ruleEvaluation.findings.map((f) => f.message);
res.status(201).json({ order: req.body, warnings });
});
app.use(rulesErrorHandler()); // a RuleViolationError thrown anywhere becomes a 422 answer
app.listen(3000);Fastify: { preHandler: rulesPreHandler(rules, { entity: 'Order', operation: 'create' }) } on the
route. Anywhere else (a NestJS service, a Next.js route handler, a queue consumer):
rules.enforce(request) returns the result or throws RuleViolationError, whose problem() is the
422 body.
Try it
curl -i -X POST localhost:3000/orders -H 'content-type: application/json' -d '{"quantity": 3}'
curl -i -X POST localhost:3000/orders -H 'content-type: application/json' -d '{"quantity": 6}'
curl -i -X POST localhost:3000/orders -H 'content-type: application/json' -d '{"quantity": 11, "note": "rush"}'| Order | Answer | Why |
|---|---|---|
| 3 items | 201 | allowed |
| 6 items | 201, with the warning "Add a note to an order of five items or more." | a warning does not block |
| 11 items | 422 application/problem+json | order.quantity.max blocks it |
The 422 body is the same in every language:
{
"type": "urn:rule-cascade:rule-violation",
"title": "Business rule violation",
"status": 422,
"detail": "Denied by 1 blocking finding: EXAMPLE-ORD-001.",
"evaluation": {
"decision": "deny",
"findings": [
{ "rule": "order.quantity.max", "code": "EXAMPLE-ORD-001", "message": "You can order at most 10 items.",
"fields": ["/quantity"], "blocking": true, "severity": "error", "status": "open", "resolution": "none",
"source": "shop.example@0.1.0" }
],
"...": "ruleset, version, checksum, effects, commands"
}
}A front end shows message next to the input that fields names (/quantity).
Change the rule, not the code
The business raises the limit to 20. In rules/example.ruleset.yaml set default: 20, and update
the golden test "eleven items are denied": 21 items, and the message "You can order at most 20
items." Then:
rcas check && rcas compile --allRestart the application (or let it reload: every runtime has a holder that refreshes bundles on a schedule). Eleven items are now accepted. No application code changed, in any language.
What you did
- Wrote a rule as data, with a parameter for the limit and tests for the examples that matter.
- Checked and compiled it into a bundle with a checksum.
- Enforced it with one line on the endpoint; the rule's answer and its 422 response are identical in TypeScript, Python, Java and Go.
Next
- The same rule in the browser, for instant feedback in a form: React form.
- A real API: actor and roles, updates with the stored record, computed values and commands after saving: backend API.
- Rules that already live in your code: existing project.
- Let your AI coding agent do it:
rcas agent installgives it the instructions and a skill for your stack: AI coding tools.
Install
Install rcas, the Rule Cascade command, on macOS, Linux or Windows, with the install script, npm or Go, and the runtime library for your language.
Start a new project
rcas init creates the project file, a first ruleset with golden tests, CI, and optionally the instructions and MCP configuration for your AI coding tools.