Policies
Combine multiple rules with evaluation strategies
Policies
Policies combine multiple rules and define an evaluation strategy.
Creating Policies
A policy groups multiple rules together:
import { definePolicy } from '@bantai-dev/core';
const securityPolicy = definePolicy(
appContext,
'security-policy',
[authRule, permissionRule, rateLimitRule],
{
defaultStrategy: 'preemptive',
}
);All rules in a policy must use the same context.
Evaluation Strategies
Bantai supports two evaluation strategies:
Preemptive Strategy
Preemptive strategy (fail-fast): Stops at first violation. Best for security checks and fast rejection:
const securityPolicy = definePolicy(
appContext,
'security-policy',
[authRule, permissionRule],
{
defaultStrategy: 'preemptive',
}
);When evaluating with preemptive strategy:
const result = await evaluatePolicy(securityPolicy, {
userId: 'user123',
role: 'user',
});
// If authRule denies, permissionRule is never evaluated
// result.violatedRules contains only the first violationUse preemptive when:
- Security is critical
- You want fast rejection
- First failure should stop evaluation
- Performance is important
Exhaustive Strategy
Exhaustive strategy: Collects all violations. Best for form validation and comprehensive feedback:
const validationPolicy = definePolicy(
appContext,
'validation-policy',
[emailRule, passwordRule, termsRule],
{
defaultStrategy: 'exhaustive',
}
);When evaluating with exhaustive strategy:
const result = await evaluatePolicy(validationPolicy, {
email: 'invalid',
password: 'weak',
termsAccepted: false,
});
// All rules are evaluated
// result.violatedRules contains all violationsUse exhaustive when:
- You want to show all errors to users
- Form validation
- Collecting comprehensive feedback
- Multiple independent checks
Overriding Strategy
You can override the default strategy when evaluating:
// Use preemptive even though policy defaults to exhaustive
const result = await evaluatePolicy(
validationPolicy,
{ email: 'test@example.com' },
{
strategy: 'preemptive',
}
);This is useful when you want different behavior for specific evaluations while keeping the default for most cases.
Policy Results
When you evaluate a policy, you get a PolicyResult:
const result = await evaluatePolicy(policy, input);
console.log(result.decision); // 'allow' or 'deny'
console.log(result.isAllowed); // true or false (convenience property)
console.log(result.reason); // 'policy_enforced' or 'policy_violated'
console.log(result.violatedRules); // Array of violated rules
console.log(result.evaluatedRules); // Array of all evaluated rules
console.log(result.strategy); // 'preemptive' or 'exhaustive'Violated Rules
The violatedRules array contains information about each rule that failed:
result.violatedRules.forEach((violation) => {
console.log(violation.name); // Rule name
console.log(violation.result.reason); // Why it failed
});Evaluated Rules
The evaluatedRules array contains information about all rules that were evaluated (both passed and failed):
result.evaluatedRules.forEach((evaluated) => {
console.log(evaluated.rule.name); // Rule name
console.log(evaluated.result.allowed); // Whether the rule allowed or denied
console.log(evaluated.result.reason); // Reason for the decision
});This is particularly useful when using the exhaustive strategy to see all rule evaluations, not just the violations.
Best Practices
- Choose the Right Strategy: Preemptive for security, exhaustive for validation
- Group Related Rules: Keep related rules together in the same policy
- Order Matters: In preemptive mode, order rules by importance
- Use Descriptive Names: Policy names should clearly indicate their purpose
Related Concepts
- Context - Policies use contexts to validate input
- Rules - Policies combine multiple rules
- Best Practices - Guidelines for effective policies