name: runtime-formulas description: Explain, debug, or extend Baserow runtime formulas, including FormulaField/JSONFormulaField storage, FormulaSerializerField validation, runtime formula functions, backend and frontend data providers, formula context dispatch data, import/export path rewriting, and tests.
Runtime Formulas
Use this skill when a task involves runtime formulas in the Application Builder, Automation, or another non-database-field surface. This includes:
- Explaining how runtime formulas are stored, validated, and resolved.
- Adding a formula-backed model field or serializer field.
- Implementing a new data provider.
- Changing the formula input data explorer.
- Adding a runtime formula function.
- Fixing formula import/export, duplication, or path migration bugs.
- Debugging formulas that use
get("provider.path").
Runtime formulas are different from database formula fields. They use the shared formula parser and runtime functions, but their data comes from application context providers instead of table rows.
Mental Model
Runtime formula resolution has four main pieces:
- The formula value is stored as a
BaserowFormulaObjectwithformula,mode, andversion. - API serializers use
FormulaSerializerFieldto normalize and validate formula objects. - Formula execution calls
resolve_formula(formula, formula_runtime_function_registry, context). get("provider.path")reads from a runtime formula context, which delegates the first path segment to a registered data provider.
Core files:
backend/src/baserow/core/formula/types.pybackend/src/baserow/core/formula/field.pybackend/src/baserow/core/formula/serializers.pybackend/src/baserow/core/formula/__init__.pybackend/src/baserow/core/formula/runtime_formula_context.pybackend/src/baserow/core/formula/registries.pybackend/src/baserow/core/formula/runtime_formula_types.pyweb-frontend/modules/core/runtimeFormulaContext.jsweb-frontend/modules/core/dataProviderTypes.jsweb-frontend/modules/core/formula/**
Formula Value Storage
Use the existing field types instead of plain text or JSON fields:
FormulaFieldstores a singleBaserowFormulaObject.JSONFormulaFieldstores nested objects/lists that contain one or moreBaserowFormulaObjectvalues.BaserowFormulaObject.create("")or an explicitBaserowFormulaObject(formula="", mode=..., version=...)is the normal default.
When exposing formula values through the API, use FormulaSerializerField.
For nested formula objects, make sure the serializer exposes each formula property
with FormulaSerializerField; collect_json_formula_field_properties() depends
on that serializer metadata for JSONFormulaField.
Important behavior:
- Empty formulas resolve to an empty string.
- Raw mode returns the raw formula string without parsing or executing it.
- Simple and advanced modes are parsed and validated.
FormulaSerializerFieldrequirescontext["application_type"]unless the formula is blank or raw, because it needs the application data provider registry to validateget(...)calls.
Runtime Formula Execution
Use resolve_formula() for backend execution:
from baserow.core.formula import resolve_formula
from baserow.core.formula.registries import formula_runtime_function_registry
result = resolve_formula(
formula_object,
formula_runtime_function_registry,
dispatch_context,
)
The context is usually a dispatch context that inherits or behaves like
RuntimeFormulaContext. Its __getitem__ receives dotted paths such as
data_source.12.field_34, splits the first segment as the provider type, and
calls that provider's get_data_chunk(dispatch_context, rest).
Frontend execution uses RuntimeFormulaContext in
web-frontend/modules/core/runtimeFormulaContext.js. It has the same provider
delegation model: context.get("provider.path") calls the registered frontend
data provider's getDataChunk(applicationContext, rest).
Runtime Functions
Runtime functions are registered in formula_runtime_function_registry.
To add or update one:
- Add a
RuntimeFormulaFunctionsubclass, usually inbackend/src/baserow/core/formula/runtime_formula_types.py. - Set a unique lowercase
type. - Define
argswithBaserowRuntimeFormulaArgumentTypeinstances, or overridevalidate_number_of_args()/validate_type_of_args()for variadic behavior. - Implement
execute(context, args). - Register it where the existing runtime functions are registered.
- Add or update frontend parser/execution/autocomplete support if the function must work client-side or appear in the formula editor.
- Add backend tests for validation and execution.
The built-in get function validates that the first path segment exists in the
active application data provider registry. If adding a provider, make sure the
provider is registered before expecting get("new_provider...") to validate.
Backend Data Providers
Backend data providers subclass DataProviderType from
backend/src/baserow/core/formula/registries.py.
Minimum implementation:
from typing import List
from baserow.core.formula.registries import DataProviderType
class ExampleDataProviderType(DataProviderType):
type = "example"
def get_data_chunk(self, dispatch_context, path: List[str]):
key, *rest = path
return ...
Implement only the hooks the provider needs:
get_data_chunk(dispatch_context, path): resolvesget("provider.path").import_path(path, id_mapping, **kwargs): rewrites IDs during import, duplication, or template install.is_valid(path): validatesget(...)paths during formula validation.extract_properties(path, **kwargs): reports service/table fields used by formulas, typically for data source dependency or property extraction.post_dispatch(dispatch_context, workflow_action, dispatch_result): runs provider-specific logic after workflow action dispatch.
Builder-specific providers usually subclass BuilderDataProviderType in:
backend/src/baserow/contrib/builder/data_providers/data_provider_types.pybackend/src/baserow/contrib/builder/data_providers/registries.py
Automation-specific providers usually subclass AutomationDataProviderType in:
backend/src/baserow/contrib/automation/data_providers/data_provider_types.pybackend/src/baserow/contrib/automation/data_providers/registries.py
Registration points:
- Builder:
backend/src/baserow/contrib/builder/apps.py - Automation:
backend/src/baserow/contrib/automation/apps.py - New application types need their own
DataProviderTypeRegistry, exposed from the application type asdata_provider_type_registry.
Follow existing provider patterns:
- Data source and previous node providers load a service/node, dispatch or read
stored results, call
service.get_type().prepare_value_path(...), then read withget_value_at_path(...). - List-returning services consume the next path segment as the row/item selector before preparing the remaining value path.
- Providers that reference IDs must implement
import_path()so copied builders, automations, templates, and imports point at the new IDs. - Providers should raise
InvalidRuntimeFormulaorInvalidFormulaContextwhen the context is malformed or references deleted objects, matching nearby code.
Dispatch Context Data
Some providers resolve entirely from backend state. Others need frontend runtime data, such as form input or page parameters.
For builder providers, implement get_request_serializer() on the backend if the
dispatch API must accept provider data. On the frontend, implement one or both:
getDataSourceDispatchContext(applicationContext)getActionDispatchContext(applicationContext)
DataProviderType.getAllDataSourceDispatchContext(...) and
DataProviderType.getAllActionDispatchContext(...) collect these objects and send
them to backend dispatch endpoints.
Keep the provider key stable. The backend receives the object under the provider
type, so type = "form_data" pairs with the frontend provider whose
static getType() returns "form_data".
Frontend Data Providers
Frontend data providers subclass DataProviderType from
web-frontend/modules/core/dataProviderTypes.js.
Minimum implementation:
import { DataProviderType } from '@baserow/modules/core/dataProviderTypes'
export class ExampleDataProviderType extends DataProviderType {
static getType() {
return 'example'
}
get name() {
return this.app.$i18n.t('dataProviderType.example')
}
getDataChunk(applicationContext, path) {
return ...
}
getDataSchema(applicationContext) {
return { type: 'object', properties: {} }
}
}
Implement these methods when relevant:
initOnce(applicationContext): load application-wide provider data.init(applicationContext): load page/node-level provider data.needBackendContext: returntrueif initialization depends on backend context.getDataChunk(applicationContext, path): resolves formulas client-side.getDataContent(applicationContext): returns current data for explorer previews.getDataSchema(applicationContext): returns JSON-schema-like data nodes forget("provider.path").getContextDataSchema(applicationContext): returns schema for request/context data supplied by the user or runtime.getPathTitle(applicationContext, pathParts): converts IDs/path tokens to user-facing names in the formula input.isValid(pathParts): rejects invalid paths in frontend validation when needed.
Registration points:
- Builder:
web-frontend/modules/builder/plugin.jsunderbuilderDataProvider. - Automation:
web-frontend/modules/automation/plugin.jsunderautomationDataProvider. - New surfaces should register a namespace and pass that registry collection into the formula input/runtime context.
Use schema objects with title, type, properties, items, and optional
order fields. The base getNodes() implementation turns these schemas into
the formula data explorer tree.
Import, Export, And Duplication
Any formula path that contains an object ID must be migrated during import or duplication.
Backend helpers and examples:
- Builder formula import:
backend/src/baserow/contrib/builder/formula_importer.py - Automation formula import:
backend/src/baserow/contrib/automation/formula_importer.py - Builder property extraction:
backend/src/baserow/contrib/builder/formula_property_extractor.py - Provider
import_path()implementations in builder and automation data providers. - Type-specific
formula_generator(...)methods on element/workflow/service types for nested formula fields.
When adding formula properties to a model/type:
- Store them with
FormulaFieldorJSONFormulaField. - Expose them with
FormulaSerializerField. - Add them to the type's formula generator/import flow if the type has custom nested formula structures.
- Implement provider
import_path()for every provider path segment that stores copied object IDs. - Add import/export or duplication tests when formulas can reference copied objects.
Common Implementation Checklist
For a new runtime formula-backed feature:
- Identify the product surface: builder, automation, dashboard, or another app.
- Choose the formula storage field:
FormulaFieldfor one value,JSONFormulaFieldfor nested repeated formula values. - Add
FormulaSerializerFieldAPI fields and pass serializer context withapplication_type. - Confirm formulas are resolved with the right dispatch context and
formula_runtime_function_registry. - If formulas need new data, add matching backend and frontend data providers.
- Register providers in the backend app config and frontend plugin.
- Add frontend schema/content/path-title methods so the data explorer and editor show the provider correctly.
- Add dispatch context serialization when backend resolution needs frontend data.
- Add import path rewriting for IDs embedded in
get(...)paths. - Add targeted backend and frontend tests.
Testing
Run the narrowest relevant tests first.
Backend areas to inspect:
backend/tests/baserow/core/formula/**backend/tests/baserow/contrib/builder/**backend/tests/baserow/contrib/automation/**- API serializer tests near the feature being changed.
- Import/export/duplication tests when paths contain IDs.
Frontend areas to inspect:
web-frontend/test/unit/core/**web-frontend/test/unit/builder/**web-frontend/test/unit/automation/**- Component tests around formula input/data explorer when schema or path titles change.
Useful commands:
just b test tests/path/to/test.pyjust f yarn test:core web-frontend/test/unit/path/to/test.spec.jsjust f test
Minimum validation before finishing:
- Backend formula validation accepts valid
get("provider.path")values and rejects invalid provider names/paths. - Backend formula execution resolves the expected values.
- Frontend formula execution resolves the same paths where client-side execution is supported.
- The formula data explorer shows useful names, schemas, and icons.
- Import/duplication rewrites ID paths correctly.
- Provider registrations exist on both backend and frontend when applicable.
Search Patterns
Use these searches before editing:
Use rg -n "<pattern>" <paths> as a faster equivalent when rg is available.
grep -RInE "class .*DataProviderType|DataProviderTypeRegistry|data_provider_type_registry" backend/src web-frontend/modules
grep -RInE "FormulaField|JSONFormulaField|FormulaSerializerField|BaserowFormulaObject" backend/src/baserow/contrib backend/src/baserow/core/formula
grep -RInE "resolve_formula\\(|formula_runtime_function_registry|RuntimeFormulaFunction" backend/src
grep -RInE "getDataChunk|getDataSchema|getDataContent|getContextDataSchema|getPathTitle" web-frontend/modules
grep -RInE "formula_generator|import_formula|import_path|extract_properties" backend/src/baserow/contrib