Expression Syntax
TinySystems uses a powerful expression language for data transformation in flow edges. Expressions are evaluated at runtime to transform data as it flows between components.
Basic Syntax
Expressions are wrapped in double curly braces:
{{expression}}Root Reference
The $ symbol refers to the message data from the source port:
{{$}} // Entire message
{{$.field}} // Field access
{{$.nested.field}} // Nested field accessPath Access
Dot Notation
{{$.user.name}}
{{$.items.length}}
{{$.config.settings.timeout}}Bracket Notation
For dynamic keys or special characters:
{{$["field-name"]}}
{{$[variableKey]}}
{{$.items[0]}}Array Access
{{$.items[0]}} // First item
{{$.items[-1]}} // Last item
{{$.items[1:3]}} // Slice (items 1 and 2)Edge Configuration
In TinyNode edges, expressions define data mapping:
yaml
edges:
- from: source-node.output
to: target-node.input
data:
# Direct expression
name: "{{$.user.name}}"
# Computed value
fullName: "{{$.firstName + ' ' + $.lastName}}"
# Static value
type: "user"
# Nested object
meta:
timestamp: "{{$.createdAt}}"
source: "api"Expression Types
Simple Path
yaml
data:
value: "{{$.data.value}}"String Concatenation
yaml
data:
greeting: "{{'Hello, ' + $.name + '!'}}"Conditional
yaml
data:
status: "{{$.active ? 'active' : 'inactive'}}"Arithmetic
yaml
data:
total: "{{$.price * $.quantity}}"
discounted: "{{$.total * (1 - $.discount)}}"Object Construction
yaml
data:
user:
id: "{{$.userId}}"
email: "{{$.userEmail}}"
active: trueSpecial Variables
Root Data
$ - The full message from source port
$.field - Access field in messageEnvironment
$.env.VARIABLE - Access environment variableContext
$.context.key - Access flow context valuesCommon Patterns
Pass-Through
Send entire message unchanged:
yaml
data: "{{$}}"Field Extraction
Extract specific fields:
yaml
data:
id: "{{$.user.id}}"
name: "{{$.user.name}}"Field Renaming
Rename fields in output:
yaml
data:
userId: "{{$.id}}" # id -> userId
userName: "{{$.name}}" # name -> userNameDefault Values
Provide defaults for missing fields:
yaml
data:
timeout: "{{$.timeout || 5000}}"
retries: "{{$.retries || 3}}"Null Coalescing
Handle null values:
yaml
data:
name: "{{$.name ?? 'Unknown'}}"Optional Chaining
Safe access to nested fields:
yaml
data:
city: "{{$.address?.city ?? 'N/A'}}"Array Mapping
Transform array items:
yaml
data:
ids: "{{$.items.map(item => item.id)}}"
names: "{{$.users.map(u => u.name)}}"Array Filtering
Filter array items:
yaml
data:
active: "{{$.users.filter(u => u.active)}}"
adults: "{{$.people.filter(p => p.age >= 18)}}"Object Spread
Merge objects:
yaml
data: "{{...$.original, timestamp: Date.now()}}"Computed Properties
Dynamic field names:
yaml
data:
"[$.fieldName]": "{{$.fieldValue}}"Visual Editor
In the visual editor, expressions are entered in the edge configuration panel:
┌────────────────────────────────────────────────────────────────┐
│ Edge: http-request.response → transform.input │
├────────────────────────────────────────────────────────────────┤
│ Data Mapping │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "status": "{{$.statusCode}}", │ │
│ │ "body": "{{$.body}}", │ │
│ │ "success": "{{$.statusCode >= 200 && $.statusCode < 300}}" │
│ │ } │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘TinyNode YAML
Full edge configuration in a TinyNode:
yaml
apiVersion: operator.tinysystems.io/v1alpha1
kind: TinyNode
metadata:
name: my-flow-abc123
spec:
component: github.com/myorg/my-module/transformer
module: my-module-v1
edges:
- from: _settings
data:
inputField: name
outputField: result
- from: upstream-node.output
to: my-node.input
data:
inputValue: "{{$.value}}"
computed: "{{$.a + $.b}}"
formatted: "{{'Result: ' + $.result}}"Expression Evaluation
┌─────────────────────────────────────────────────────────────────────────────┐
│ EXPRESSION EVALUATION │
└─────────────────────────────────────────────────────────────────────────────┘
Source Port Output Edge Expression Target Port Input
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ { │ │ data: │ │ { │
│ "name": │ ─────▶ │ name: {{$.name}} ─────▶ │ "name": │
│ "John" │ │ upper: {{...}} │ │ "John", │
│ } │ └──────────────────┘ │ "upper": │
└─────────────┘ │ "JOHN" │
│ } │
└─────────────┘Error Handling
Invalid Path
yaml
# If $.nonexistent doesn't exist, result is undefined
data:
value: "{{$.nonexistent}}" # undefinedSafe Access
yaml
# Use optional chaining for safety
data:
value: "{{$.maybe?.nested?.field ?? 'default'}}"Type Coercion
yaml
# String to number
data:
count: "{{parseInt($.countString)}}"
# Number to string
data:
label: "{{String($.count)}}"Performance Tips
- Keep Expressions Simple: Complex expressions run on every message
- Avoid Deep Nesting:
$.a.b.c.d.eis slower than flat structures - Use Filtering Carefully: Large array operations can be slow
- Cache Computed Values: Use intermediate nodes for reused computations
Next Steps
- Data Transformation - Common transformations
- Built-in Functions - Available functions
- Ports and Messages - Message structure