Your browser does not support the video tag.
36.1 Overview
AEL (ADL Expression Language) is a lightweight, sandboxed expression language embedded in the ADL runtime. It evaluates expressions in four contexts:
Context Use Cases Validation Cross-field validation rules (§14) Permissions RBAC expressions (§11) Computed Fields Calculated values (§13) Guards State machine conditions (§12)
AEL is not a general-purpose scripting language. It has no assignment, no loops, no mutation, and no I/O. Every expression is a pure function of its input context.
36.2 Grammar (EBNF)
expression = conditional ;
conditional = null_coalesce ( "?" expression ":" expression )? ;
null_coalesce = logical_or ( "??" logical_or )* ;
logical_or = logical_and ( "||" logical_and )* ;
logical_and = equality ( "&&" equality )* ;
equality = comparison ( ( "==" | "!=" ) comparison )* ;
comparison = additive ( ( ">" | ">=" | "<" | "<=" | "in" ) additive )* ;
additive = multiplicative ( ( "+" | "-" ) multiplicative )* ;
multiplicative = unary ( ( "*" | "/" | "%" ) unary )* ;
unary = ( "!" | "-" ) unary | postfix ;
postfix = primary ( "." IDENT | "?." IDENT | "[" expression "]"
| "(" arg_list? ")" )* ;
primary = NUMBER | STRING | "true" | "false" | "null"
| IDENT | "(" expression ")" | array | object
| lambda ;
array = "[" ( expression ( "," expression )* )? "]" ;
object = "{" ( object_pair ( "," object_pair )* )? "}" ;
object_pair = ( IDENT | STRING ) ":" expression ;
lambda = IDENT "=>" expression
| "(" param_list? ")" "=>" expression ;
36.3 Literals
Type Examples Number 42, 3.14, -1, 1e6String "hello", 'world'Boolean true, falseNull nullArray [1, 2, 3], ["a", "b"]Object { name: "Alice", age: 30 }
36.4 Operators
Arithmetic
+ Addition 2 + 3 → 5
- Subtraction 5 - 2 → 3
* Multiplication 3 * 4 → 12
/ Division 10 / 2 → 5
% Modulo 10 % 3 → 1
Comparison
== Equal 5 == 5 → true
!= Not equal 5 != 3 → true
< Less than 3 < 5 → true
<= Less or equal 5 <= 5 → true
> Greater than 5 > 3 → true
>= Greater or equal 5 >= 5 → true
in Membership "a" in ["a", "b"] → true
Logical
&& Logical AND true && false → false
|| Logical OR true || false → true
! Logical NOT !true → false
Special
?? Nullish coalesce null ?? "default" → "default"
? : Ternary age >= 18 ? "adult" : "minor"
?. Optional chain user?.address?.city
Property Access
. Dot notation record.title
[] Bracket notation record["field-name"]
Operator Precedence (highest to lowest)
Property access (., ?., []), function calls, grouping ()
Unary (!, -)
Multiplicative (*, /, %)
Additive (+, -)
Comparison (<, <=, >, >=, in)
Equality (==, !=)
Logical AND (&&)
Logical OR (||)
Nullish coalescing (??)
Ternary (? :)
36.5 Context Variables
Variables available depend on the evaluation context:
Validation Context
Variable Type Description Field names any Direct field access: startDate, price, status $thisobject Merged record state (existing + new values) $oldobject|null Record before update (null on create) $opstring Operation: "create" or "update"
Example:
validation :
dateOrder :
rule : "endDate > startDate"
futureOnly :
rule : "$op != 'create' || startDate > now()"
Permission Context
Variable Type Description userobject Current user with id, username, email, roles recordobject Record being accessed
Example:
permissions :
update : "user.id == record.authorId || hasRole('admin')"
Guard Context (State Machines)
Variable Type Description recordobject Current record userobject User attempting transition transitionstring Transition name being attempted
Example:
transitions :
publish :
from : [ draft , review ]
to : published
guard : "hasRole('editor') && record.reviewedBy != null"
Computed Field Context
Variable Type Description $thisobject Current record Field names any Direct field access
Example:
computed :
fullName :
type : string
formula : "concat(firstName, ' ', lastName)"
36.6 Built-In Functions
String Functions (21)
Function Example Result len(s)len("hello")5upper(s)upper("hello")"HELLO"lower(s)lower("WORLD")"world"trim(s)trim(" hi ")"hi"substring(s, start, end?)substring("hello", 0, 3)"hel"startsWith(s, prefix)startsWith("hello", "he")trueendsWith(s, suffix)endsWith("hello", "lo")truecontains(s, sub)contains("hello", "ell")trueindexOf(s, sub)indexOf("hello", "l")2replace(s, search, repl)replace("hi", "i", "o")"ho"split(s, delim)split("a,b,c", ",")["a","b","c"]join(arr, delim)join(["a","b"], ",")"a,b"concat(...)concat("a", " ", "b")"a b"padStart(s, len, pad?)padStart("5", 3, "0")"005"padEnd(s, len, pad?)padEnd("5", 3, "0")"500"capitalize(s)capitalize("hello")"Hello"camelCase(s)camelCase("hello world")"helloWorld"snakeCase(s)snakeCase("HelloWorld")"hello_world"
Array Functions (21)
Function Example Result len(arr)len([1, 2, 3])3first(arr)first([1, 2, 3])1last(arr)last([1, 2, 3])3at(arr, index)at([1, 2, 3], 1)2slice(arr, start, end?)slice([1,2,3,4], 1, 3)[2,3]concat(arr, ...arrs)concat([1], [2], [3])[1,2,3]reverse(arr)reverse([1, 2, 3])[3,2,1]sort(arr)sort([3, 1, 2])[1,2,3]unique(arr)unique([1,2,2,3])[1,2,3]flatten(arr, depth?)flatten([[1,2],[3,4]])[1,2,3,4]includes(arr, val)includes([1,2,3], 2)truemap(arr, fn)map([1,2,3], x => x*2)[2,4,6]filter(arr, fn)filter([1,2,3], x => x>1)[2,3]find(arr, fn)find([1,2,3], x => x>1)2every(arr, fn)every([2,4,6], x => x%2==0)truesome(arr, fn)some([1,2,3], x => x>2)truereduce(arr, fn, init)reduce([1,2,3], (a,b)=>a+b, 0)6sum(arr)sum([1, 2, 3])6avg(arr)avg([1, 2, 3])2min(arr)min([3, 1, 2])1max(arr)max([3, 1, 2])3
Math Functions (14)
Function Description abs(n)Absolute value ceil(n)Round up floor(n)Round down round(n, decimals?)Round to nearest sqrt(n)Square root pow(base, exp)Power log(n)Natural logarithm exp(n)e^n sin(n), cos(n), tan(n)Trigonometric min(...nums)Minimum max(...nums)Maximum random()Random 0-1 randomInt(min, max)Random integer
Date Functions (15)
Function Example Result now()now()Current timestamp today()today()Current date (midnight) dateAdd(date, n, unit)dateAdd(today(), 7, 'day')7 days from now dateSub(date, n, unit)dateSub(today(), 1, 'month')1 month ago dateDiff(d1, d2, unit)dateDiff(endDate, startDate, 'day')Days between year(date)year(today())2026month(date)month(today())1-12day(date)day(today())1-31hour(date)hour(now())0-23minute(date)minute(now())0-59second(date)second(now())0-59dayOfWeek(date)dayOfWeek(today())0-6 (Sun-Sat)isWeekend(date)isWeekend(today())true/falseformatDate(date, fmt)formatDate(today(), 'yyyy-MM-dd')"2026-05-17"parseDate(str, fmt)parseDate("2026-05-17", 'yyyy-MM-dd')date value
Type Functions (8)
Function Description type(val)Returns "string", "number", "boolean", "null", "array", "object" isString(val)Test if string isNumber(val)Test if number isBoolean(val)Test if boolean isNull(val)Test if null isArray(val)Test if array isObject(val)Test if object isDefined(val)Test if not null/undefined
ADL Context Functions
Authentication (3)
Function Description hasRole(role)Check if user has role isOwner()Check if user owns record (user.id == record.createdBy) isAuthenticated()Check if request has valid token
State Machine (3)
Function Description inState(state)Check if record is in state canTransition(transition)Check if transition is available currentState()Get current state value
Field Change (4)
Function Description fieldChanged(field)Check if field changed (update only) oldValue(field)Get previous value (update only) newValue(field)Get new value (update only) isCreate()Check if operation is create isUpdate()Check if operation is update
Query (2)
Function Description query(resource, filter)Query records (validation context only) exists(resource, filter)Check if record exists (validation context only)
36.7 Lambda Expressions
AEL supports lambda expressions for collection operations:
Single parameter: x => x * 2
Multiple parameters: (a, b) => a + b
Example usage:
map([1, 2, 3], x => x * 2) → [2, 4, 6]
filter(items, x => x.price > 10) → items with price > 10
reduce([1, 2, 3], (sum, x) => sum + x, 0) → 6
36.8 Security Limits
Limit Default Purpose Max expression length 10,000 chars Prevent parser DoS Max nesting depth 32 levels Prevent stack overflow Max array size 10,000 elements Prevent memory exhaustion Query timeout 5 seconds Prevent long-running queries Query result limit 1,000 rows Prevent memory exhaustion
36.9 Error Handling
Syntax Errors
Expression: "user.name +"
Error: Unexpected end of expression at line 1, column 12
Runtime Errors
Expression: "1 / 0"
Error: Division by zero
Expression: "user.address.city"
Context: { user: { name: "Alice" } }
Error: Cannot read property 'city' of undefined
Type Errors
Expression: "5 + 'hello'"
Error: Cannot add number and string
36.10 Integration Points
Feature AEL Usage Validation (§14)Cross-field validation rules Permissions (§11)RBAC expressions with role/owner checks Computed Fields (§13)Formula evaluation State Machines (§12)Guard conditions on transitions Rules (§22)Business rule conditions Workflows (§25)Step conditions and data flow Scopes (§20)Filter expressions Integrations (§35)Data mapping and transform functions Reports (§26)Highlight conditions and computed columns Privacy (§33)Anonymization rules
End of Chapter 36.
Next: Chapter 37 — OpenAPI Generation