FEEL Function Reference
Forma uses FEEL (Friendly Enough Expression Language) for dynamic expressions. This page provides a complete reference of available functions and operators.
Context Variables
In FEEL expressions, you can access form data using these patterns:
| Pattern | Description | Example |
|---|---|---|
fieldName | Direct field access | age >= 18 |
computed.name | Computed field value | computed.bmi > 25 |
ref.path | Reference data lookup | ref.taxRates.CA |
value | Current field value (in validation) | value >= 0 |
item.fieldName | Array item field (in array context) | item.quantity > 0 |
itemIndex | Array item index (0-based) | itemIndex = 0 |
Operators
Arithmetic
| Operator | Description | Example |
|---|---|---|
+ | Addition | price + tax |
- | Subtraction | total - discount |
* | Multiplication | quantity * unitPrice |
/ | Division | total / count |
** | Exponentiation | base ** 2 |
Comparison
| Operator | Description | Example |
|---|---|---|
= | Equal | status = "active" |
!= | Not equal | status != "archived" |
> | Greater than | age > 18 |
>= | Greater or equal | age >= 21 |
< | Less than | price < 100 |
<= | Less or equal | price <= 50 |
Logical
| Operator | Description | Example |
|---|---|---|
and | Both conditions true | age >= 18 and hasLicense |
or | Either condition true | status = "active" or status = "pending" |
not | Negation | not(isBlocked) |
String Functions
string length
Returns the length of a string.
{
"rule": "string length(value) >= 3",
"message": "Must be at least 3 characters"
}
substring
Extracts a portion of a string.
substring(string, start)
substring(string, start, length)
{
"expression": "substring(phone, 1, 3)"
}
Note: FEEL uses 1-based indexing for strings.
upper case / lower case
Convert string case.
{
"expression": "upper case(code)",
"expression": "lower case(email)"
}
contains
Check if a string contains a substring.
{
"visibleWhen": "contains(email, \"@company.com\")"
}
starts with / ends with
Check string prefix or suffix.
{
"visibleWhen": "starts with(phone, \"+1\")"
}
{
"visibleWhen": "ends with(email, \".edu\")"
}
matches
Test a string against a regular expression pattern.
{
"rule": "matches(value, \"^[A-Z]{2}[0-9]{4}$\")",
"message": "Must be 2 letters followed by 4 digits (e.g., AB1234)"
}
String Concatenation
Use + to concatenate strings.
{
"expression": "firstName + \" \" + lastName"
}
Numeric Functions
abs
Returns the absolute value.
{
"expression": "abs(difference)"
}
floor / ceiling
Round down or up to nearest integer.
{
"expression": "floor(price)",
"expression": "ceiling(quantity)"
}
round
Round to specified precision.
round(number)
round(number, scale)
{
"expression": "round(total, 2)"
}
min / max
Find minimum or maximum in a list.
{
"expression": "min([price1, price2, price3])",
"expression": "max([score1, score2])"
}
sum
Sum all values in a list.
{
"expression": "sum([item1, item2, item3])"
}
mean
Calculate the average of a list.
{
"expression": "mean([score1, score2, score3])"
}
Date Functions
today
Returns the current date.
{
"rule": "date(value) > today()",
"message": "Date must be in the future"
}
now
Returns the current datetime.
{
"rule": "date and time(value) < now()",
"message": "Must be a past datetime"
}
date
Parses a string into a date.
{
"expression": "date(dateOfBirth)",
"rule": "date(endDate) > date(startDate)"
}
Date Components
Extract parts from a date.
{
"expression": "year(date(dateOfBirth))",
"expression": "month(date(startDate))",
"expression": "day(date(endDate))"
}
Date Arithmetic
Subtract dates to get duration.
{
"expression": "date(endDate) - date(startDate)"
}
List Functions
count
Returns the number of items in a list.
{
"visibleWhen": "count(selectedItems) > 0"
}
all / any
Check if all or any items match a condition.
{
"rule": "all items in items satisfy item.quantity > 0",
"message": "All items must have positive quantity"
}
append
Add an item to a list.
{
"expression": "append(existingList, newItem)"
}
flatten
Flatten nested lists.
{
"expression": "flatten([[1, 2], [3, 4]])"
}
Reference Data Functions
get value
Dynamic lookup in reference data using a field value as the key.
{
"referenceData": {
"taxRates": {
"CA": 0.0725,
"NY": 0.08,
"TX": 0.0625
}
},
"computed": {
"stateTax": {
"expression": "get value(ref.taxRates, state)"
}
}
}
Static Path Access
For fixed paths, use dot notation.
{
"expression": "ref.constants.taxRate"
}
Nested Lookup
Access nested properties from dynamic lookups.
{
"referenceData": {
"products": {
"SKU001": { "price": 9.99, "weight": 0.5 },
"SKU002": { "price": 19.99, "weight": 1.2 }
}
},
"computed": {
"productPrice": {
"expression": "get value(ref.products, selectedSku).price"
}
}
}
Conditional Expressions
if-then-else
Basic conditional.
{
"expression": "if age >= 18 then \"adult\" else \"minor\""
}
Chained conditions
{
"expression": "if score >= 90 then \"A\" else if score >= 80 then \"B\" else if score >= 70 then \"C\" else \"F\""
}
Null Handling
Null Check
{
"visibleWhen": "email != null"
}
Safe Operations
Handle null values with conditionals.
{
"expression": "if quantity != null then quantity * price else 0"
}
Null Coalescing
{
"expression": "if discountRate = null then 0 else discountRate"
}
Common Patterns
Age Calculation
{
"expression": "year(today()) - year(date(dateOfBirth))"
}
Percentage Calculation
{
"expression": "round((completed / total) * 100, 1)"
}
Conditional Discount
{
"expression": "if subtotal >= 100 then subtotal * 0.9 else subtotal"
}
Status Based on Value
{
"expression": "if computed.bmi < 18.5 then \"Underweight\" else if computed.bmi < 25 then \"Normal\" else if computed.bmi < 30 then \"Overweight\" else \"Obese\""
}
Expression Debugging
If your expression isn't working as expected:
- Check field names - Ensure exact spelling match
- Verify data types - Numbers and strings behave differently
- Handle nulls - Unset fields return null
- Test incrementally - Start simple and add complexity
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Expression returns null | Field value is null | Add null check |
| Type mismatch | Comparing number to string | Use consistent types |
| Field not found | Typo in field name | Check field exists in schema |
| Syntax error | Invalid FEEL syntax | Check quotes and operators |