21 January, 2026
This release unifies the expression system across Keel, making all arithmetic operators, aggregate functions, and scalar functions work consistently in @where, @set, @permission, and @computed expressions.
Unified Expression System
Expressions now work consistently across all contexts. Arithmetic operators, aggregate functions, and scalar functions that previously only worked in computed fields can now be used in permissions, filters, and set expressions.
Arithmetic in Permissions
You can now use calculations in permission rules to control access based on computed values:
model Budget {
fields {
limit Decimal
spent Decimal
}
actions {
list listBudgetsWithBalance() {
// Permission based on arithmetic
@permission(expression: budget.limit - budget.spent > 100)
}
}
}Aggregate Functions in Filters
Use aggregate functions in @where expressions to filter based on related data:
model Customer {
fields {
orders Order[]
}
actions {
list listHighValueCustomers() {
// Filter customers by total order value
@where(SUM(customer.orders.total) > 1000)
}
list listFrequentBuyers() {
// Filter by number of orders
@where(COUNT(customer.orders.id) >= 5)
}
}
}Conditional Logic in Set Expressions
Apply conditional updates using the IF function in @set expressions:
model Product {
fields {
isActive Boolean
price Decimal
finalPrice Decimal
}
actions {
update applyDiscount(id) {
// Set different prices based on conditions
@set(product.finalPrice = IF(product.isActive, product.price * 0.9, product.price))
}
}
}This unification means you can now use the full expression feature set—including SUM, COUNT, AVG, MIN, MAX, MEDIAN, conditional variants like SUMIF and COUNTIF, date functions like YEAR, MONTH, ADDDAYS, and the IF function—across all expression contexts.
New Scalar Functions
This release also introduces scalar functions for conditional logic and date operations:
| Function | Description | Example |
|---|---|---|
IF | Returns one value if condition is true, another if false | IF(order.isPriority, "Rush", "Standard") |
YEAR | Extracts the year from a date or timestamp | YEAR(order.createdAt) |
MONTH | Extracts the month (1-12) from a date or timestamp | MONTH(order.createdAt) |
DAY | Extracts the day of month (1-31) from a date or timestamp | DAY(order.createdAt) |
HOUR | Extracts the hour (0-23) from a timestamp | HOUR(order.createdAt) |
MINUTE | Extracts the minute (0-59) from a timestamp | MINUTE(order.createdAt) |
SECOND | Extracts the second (0-59) from a timestamp | SECOND(order.createdAt) |
ADDDAYS | Adds or subtracts days from a date or timestamp | ADDDAYS(order.dueDate, 7) |
ADDMONTHS | Adds or subtracts months from a date or timestamp | ADDMONTHS(subscription.startDate, 1) |
ADDYEARS | Adds or subtracts years from a date or timestamp | ADDYEARS(contract.startDate, 2) |
DIFF | Calculates the difference in days between two dates | DIFF(task.deadline, task.startedAt) |
See the Expressions reference for the complete list of supported functions and detailed usage examples.
Schema Formatting with keel format
You can now automatically format your Keel schema files using the new keel format command. This ensures consistent code style across your project.
keel formatThe command formats all .keel files in your project, standardizing indentation, spacing, and structure. This is especially useful for team projects where maintaining consistent formatting matters. You can also integrate it into your pre-commit hooks or CI pipeline to enforce formatting standards.
Learn more about Keel CLI commands in the CLI documentation.
Automatic Foreign Key Indexes
Keel now automatically creates database indexes for all foreign key columns during migrations. This improves query performance for relationship lookups without any changes to your schema.
For example, when you define a relationship like this:
model Order {
fields {
customer Customer
items OrderItem[]
}
}
model OrderItem {
fields {
order Order
product Product
}
}Keel will now automatically create indexes on the customerId column in the order table and on the orderId and productId columns in the order_item table. These indexes make queries that filter or join on relationships significantly faster, especially as your data grows.
This optimization happens automatically in new migrations—no action needed on your part. For more on relationships and how Keel generates database tables, see the Models documentation.
Fixes and Improvements
- Fixed permission logic for flows and tasks with multiple
@permissionrules. Multiple rules now correctly use OR logic, meaning access is granted if any of the permission conditions are met. - Fixed validation for computed field expressions to prevent using model type names directly in expressions.
For a full list of fixes and improvements, check out our GitHub releases page (opens in a new tab).
For any issues or feedback, please contact us at help@keel.so.
Thank you for using Keel!