writing-handlebars

Write Handlebars template expressions for Celigo integrations -- dynamic values in mappings, HTTP bodies, SQL queries, URIs, and filters. Use when building any resource configuration that needs computed, conditional, or formatted field values.

By celigo · 1,050 installs

npx skills add celigo/ai --skill writing-handlebars

Source repository · Upstream listing

<! TIER:1 Writing Handlebars Expressions Handlebars is Celigo's template language for embedding dynamic values into resource configurations. Any string field that the platform evaluates at runtime can contain Handlebars expressions. Concerns when writing Handlebars: Context where the expression runs determines what data is available and how output is treated Braces double {{ }} vs triple {{{ }}} controls output escaping Field access record. prefix in all contexts (AFE 2.0), @root for job/settings/connection, bracket notation for special characters Helpers 79 custom helpers for math, string manipulation, dates, encoding, regex, and more Block helpers each , if , compare , with for iteration and conditional logic Date/time moment.js format tokens with timezone support Used across exports, imports, mappings, output filters, and APIs. Where Handlebars Are Used Mapping extracts In import mappings[].extract fields, Handlebars concatenates, transforms, or conditionally selects values. The context is the current record. HTTP request templates Export and import http blocks use Handlebars in relativeURI , body , headers , and postBody . Triple braces are essential to avoid HTML encoding of query parameters and JSON. RDBMS SQL queries SQL queries in rdbms.query use Handlebars with the mandatory record. prefix. Triple braces prevent encoding of SQL significant characters like commas and quotes. For full SQL patterns (MERGE, upsert, bulk operations, dialect differences), see [writing sql](../writing sql/SKILL.md). Output filters Expression based filters on exports use Handlebars to evaluate whether a record passes through or gets skipped. File paths and names Dynamic file names in FTP/S3 exports and imports use Handlebars for timestamps and record derived values. Delta tokens Platform injected variables like {{{lastExportDateTime}}} provide the last successful export timestamp for incremental syncs. These are not record fields the platform injects them at runtime into the export's HTTP/query context only. Quick Reference Context Decision Matrix (AFE 2.0) All contexts use record. prefix to access the current record's fields (AFE 2.0). Do NOT use bare field names, data.field , or data.0.field those are deprecated AFE 1.0 patterns. Exception: Mapper 1.0 (Salesforce/NetSuite) uses bare field names without record. prefix. Where Syntax Data prefix Example Mapping extract {{ }} (double) record. {{record.firstName}} HTTP relative URI {{{ }}} (triple) record. {{{record.orderId}}} in URI HTTP body / postBody {{{ }}} (triple) record. {{{record.orderId}}} in JSON body SQL query (RDBMS) {{{ }}} (triple) record. {{{record.email}}} in WHERE clause Output filter {{ }} (double) record. {{record.status}} Delta URI parameter {{{ }}} (triple) (platform injected) {{{lastExportDateTime}}} Additional context objects available via @root : Object Description record Current record being processed job Current job metadata settings Integration/flow settings connection Connection object (for auth headers) When one to many grouping is configured, the data shape changes to batch of records iterate with {{ each batch of records}} to access individual records. Key Syntax {{{triple braces}}} raw output, no escaping. Use for URIs, SQL, JSON bodies, file paths anywhere commas, quotes, or ampersands matter. In RDBMS , triple braces output the raw value ( value ); double braces wrap in single quotes ( 'value' ). Prefer triple and add literal quotes explicitly where needed. {{double braces}} context dependent formatting. In RDBMS adds single quotes around the value. In URLs, URL encodes. Use triple braces for explicit control. Always use record. prefix (AFE 2.0) {{{record.fieldName}}} in all contexts, never bare {{{fieldName}}} or {{{data.fieldName}}} (AFE 1.0). Nested fields: {{{record.properties.email}}} . Exception: Mapper 1.0 (Salesforce/NetSuite) uses bare field names without record. prefix. This is the only context where bare field references are correct. Related Skills [configuring exports Quick Reference](../configuring exports/SKILL.md quick reference) export adaptor types, delta sync setup, output filters [configuring imports Quick Reference](../configuring imports/SKILL.md quick reference) import adaptor types, operation modes, mapping systems [writing mappings Quick Reference](../writing mappings/SKILL.md quick reference) Mapper 2.0 fields, lookups, conditional mappings <! TIER:2 Syntax Fundamentals Braces Syntax Behavior When to use {{ }} Context dependent formatting RDBMS wraps value in single quotes ( 'value' ), URLs get URL encoded Use only when auto formatting is desired {{{ }}} Raw output, no escaping or wrapping Prefer everywhere SQL, JSON bodies, URIs, file paths. Add literal quotes yourself where needed {{{{ }}}} Raw block contents treated as literal string Escaping Handlebars syntax itself Field access Pattern Meaning record.fieldName Standard field reference all contexts (AFE 2.0) record.nested.field Dot notation for nested objects record.[Field With Spaces] Bracket notation for special characters in field names record.items.[0].name Array index access @root.fieldName Root context escape nested each scope ../fieldName Parent context one level up from current each this Current iteration element @index / @key Current array index / object key in each @first / @last Boolean first/last element in each iteration Subexpressions (nesting helpers) Use () to nest one helper's output as input to another. The inner helper evaluates first: Subexpressions can be nested multiple levels deep. Each () resolves inside out. Block helpers {{ each record.items}}...{{/each}} iterate array or object {{ if record.active}}...{{else}}...{{/if}} conditional {{ compare val1 "==" val2}}...{{/compare}} comparison ( == , === , != , !== , < , , <= , = ) {{ with record.address}}...{{/with}} change context scope Date/time formatting Uses moment.js tokens. Always use triple braces for date output. Common tokens: YYYY (4 digit year), MM (2 digit month), DD (2 digit day), HH (24h hour), mm (minute), ss (second), SSS (millisecond), Z (timezone offset), X (Unix seconds), x (Unix milliseconds). Timezone: pass as third argument {{{dateFormat "YYYY MM DD" record.date "US/Eastern"}}} . Date arithmetic dateAdd works in milliseconds : 1 hour = 3,600,000 1 day = 86,400,000 7 days = 604,800,000 Runtime Context at Each Stage What {{record.X}} or {{settings.Y}} actually resolves to depends on which bubble the expression runs in. The shapes below were captured by setting body: "{{{jsonSerialize this}}}" on import/lookup bubbles and echoing through a mirror endpoint — they represent exactly what's available at runtime. Import bubble (HTTPImport, NetSuiteDistributedImport, etc.) Body templates and Handlebars in mappings[].extract run per record with this context: Lookup bubble (HTTPExport with isLookup: true ) Lookup request templates run per record with a different shape: Export bubble (source generator) Export URI templates and delta tokens have a minimal context — the platform injects {{{lastExportDateTime}}} , {{{currentExportDateTime}}} , plus settings and connection . No record. context exists yet (records haven't been fetched). Key differences between import and lookup contexts Context key Import Lookup Record location 0.<field + data[].<field <field (top level) + data.<field data shape array of records single record (with INITDATA nested) exportStartTime No Yes lookup Yes (merged preceding results) N/A import / job / recordLookupError / testMode Yes No connection (full) Yes Yes How to rediscover the shape for any bubble Set the body on an HTTP import or lookup to {{{jsonSerialize this}}} and point it at an echo endpoint (integrator.io's /v1/mirror works). Enable flow execution logging, run the flow, and inspect the apiCall.response.body — it's a copy of what you sent, which is the full runtime context. This works for any bubble whose adaptor sends an HTTP body. How to Write a Handlebars Expression 1. Identify the context Where the expression runs determines what data is available. In AFE 2.0, all contexts use record. to access the current record: Context Available data Prefix Mapping extract Current record record. HTTP body/URI Current record record. RDBMS query Current record record. Output filter Current record record. Delta URI parameter Platform variables lastExportDateTime , lastExportDateTimeUTC Other context objects ( job , settings , connection ) are accessible via @root e.g., {{@root.connection.http.encrypted.apiKey}} . When one to many grouping is active, the shape is batch of records and you must iterate: {{ each batch of records}}{{record.field}}{{/each}} . 2. Know the data shape Before writing any expression, inspect what the input data looks like: 3. Choose the right braces Default to {{{ }}} (triple) for HTTP bodies, SQL, URIs, file paths Use {{ }} (double) only in mapping extracts and display text where HTML escaping is acceptable When in doubt, use triple raw output never breaks SQL or JSON; HTML escaped output can 4. Find the right helper See the [helper index](references/helpers/helper index.md) for all 79 custom helpers. Key categories: [Math](references/helpers/math.md) abs , add , subtract , multiply , divide , modulo , ceil , floor , round , sum , avg , random , toFixed , toExponential , toPrecision [String](references/helpers/string.md) uppercase , lowercase , capitalize , capitalizeAll , camelcase , pascalcase , snakecase , dashcase , dotcase , pathcase , sentence , trim , trimLeft , trimRight , padLeft , padRight , replace , replacefirst , removefirst , chop , truncateWords , sanitize , split , join , reverse , occurrences , substring [Array](references/helpers/array.md) after , before , first , last , reverse , sort , unique , pluck , arrayify , lookup , getValue , sum [Date/time](references/helpers/date.md) dateFormat , dateAdd , timestamp [Encoding](references/helpers/encoding.md) base64Encode , base64Decode , htmlEncode , htmlDecode , jsonEncode , jsonParse , jsonSerialize , encodeURI , decodeURI , stripProtocol , stripQuerystring [Regex](references/helpers/regex.md) regexMatch , regexReplace , regexSearch [Auth/crypto](references/helpers/auth.md) hash , hmac , aws4 [Type/logic](references/helpers/type logic.md) typeOf , eq , isTruthy , isFalsey , hasOwn , hasNoItems , compare [Format](references/helpers/format.md) addCommas , bytes , ordinalize [Block helpers](references/helpers/block helpers.md) each , if , compare , contains , filter , and , or , not , unless , with , some , startsWith , inArray , isEmpty 5. Test the expression Common Patterns JSON comma separation in HTTP body templates Avoid trailing commas when building JSON arrays: Grouped data access (one to many / batch of records) When one to many grouping is configured, the data shape becomes batch of records . Iterate to access individual records: Conditional field with fallback Nested iteration with parent context SQL