Object Control
Object controls provide a JSON editor for supplying structured data — objects, arrays, or any JSON-serializable value. They're ideal for HTTP headers, custom metadata, configuration maps, and typed arrays where users need to supply composite values rather than a single scalar.
Basic Usage
import * as i from '@xentom/integration-framework';
// Plain object input
i.controls.object({
label: 'Custom Headers',
placeholder: '{ "X-Custom-Header": "value" }',
});
// With a default value
i.controls.object({
label: 'Default Config',
default: { retries: 3, timeout: 5000 },
rows: 6,
});Configuration Options
Prop
Type
Important Note
Object controls parse their value as JSON, not as JavaScript. The editor accepts any valid JSON literal — objects, arrays, strings, numbers, booleans, and null — but does not evaluate expressions or execute code. If you need evaluated JavaScript, use an Expression Control instead.
Typed Arrays
ObjectControlBuilder is generic, so you can constrain the expected value type at the call site. The type parameter flows through to default and the rest of the integration's type system:
// Typed string array
tags: i.pins.data<string[]>({
control: i.controls.object<string[]>({
label: 'Tags',
description: 'List of tags to attach to the record',
default: [],
}),
});
// Typed object array
recipients: i.pins.data<{ email: string; name: string }[]>({
control: i.controls.object<{ email: string; name: string }[]>({
label: 'Recipients',
default: [{ email: '[email protected]', name: 'User' }],
rows: 6,
}),
});Common Use Cases
HTTP Headers
headers: i.pins.data({
control: i.controls.object({
label: 'HTTP Headers',
description: 'Additional headers to include with every request',
placeholder: '{\n "X-Api-Version": "2",\n "Accept-Language": "en-US"\n}',
default: {},
rows: 6,
}),
});Custom Metadata
metadata: i.pins.data({
control: i.controls.object({
label: 'Metadata',
description: 'Arbitrary key-value pairs attached to the record',
default: {},
rows: 4,
}),
});Tag Lists
tags: i.pins.data<string[]>({
control: i.controls.object<string[]>({
label: 'Tags',
description: 'List of tags, e.g. ["billing", "urgent"]',
default: [],
}),
});Nested Configuration
// Environment variable for a complex config block
SMTP_OPTIONS: i.env({
control: i.controls.object({
label: 'SMTP Options',
description: 'Additional options passed directly to the SMTP client',
default: { pool: true, maxConnections: 5 },
rows: 8,
}),
});Usage in Nodes
Object controls work in both environment variables and node pins:
export default i.integration({
env: {
// Global object setting shared across all nodes
DEFAULT_HEADERS: i.env({
control: i.controls.object({
label: 'Default Headers',
description: 'Headers included in every outgoing request',
default: { 'Content-Type': 'application/json' },
rows: 6,
}),
}),
},
nodes: {
sendRequest: i.nodes.action({
inputs: {
// Per-node object inputs
body: i.pins.data({
control: i.controls.object({
label: 'Request Body',
description: 'JSON body to send with the request',
placeholder: '{\n "key": "value"\n}',
rows: 10,
}),
}),
extraHeaders: i.pins.data({
control: i.controls.object({
label: 'Extra Headers',
description: 'Additional headers merged with the defaults',
default: {},
rows: 4,
}),
}),
},
outputs: {
response: i.pins.data(),
statusCode: i.pins.data(),
},
async run({ inputs, env, next }) {
const headers = {
...env.DEFAULT_HEADERS,
...inputs.extraHeaders,
};
const response = await fetch(apiUrl, {
method: 'POST',
headers,
body: JSON.stringify(inputs.body),
});
next({
response: await response.json(),
statusCode: response.status,
});
},
}),
},
});Best Practices
Provide a Meaningful Placeholder
Show users what a valid value looks like directly in the placeholder — especially helpful for nested structures:
// Good - placeholder shows the expected shape
connectionOptions: i.pins.data({
control: i.controls.object({
label: 'Connection Options',
placeholder: '{\n "ssl": true,\n "timeout": 5000\n}',
}),
});
// Avoid - placeholder gives no structural hint
connectionOptions: i.pins.data({
control: i.controls.object({
label: 'Connection Options',
placeholder: 'Enter JSON...', // Not helpful
}),
});Supply a Default Value
When a sensible empty state exists, set it as the default so users can see the expected type at a glance:
// Empty object as default — signals "add your key-value pairs here"
queryParams: i.pins.data({
control: i.controls.object({
label: 'Query Parameters',
default: {},
}),
});
// Empty array as default — signals "add items to this list"
allowedIps: i.pins.data<string[]>({
control: i.controls.object<string[]>({
label: 'Allowed IPs',
default: [],
}),
});Adjust Rows for Expected Complexity
Use rows to give users enough vertical space without overwhelming the form:
// Small objects — fewer rows
labels: i.pins.data({
control: i.controls.object({
label: 'Labels',
rows: 3,
}),
});
// Deeply nested or large objects — more rows
policyDocument: i.pins.data({
control: i.controls.object({
label: 'Policy Document',
description: 'Full IAM policy document in JSON format',
rows: 20,
}),
});Prefer Expression Control for Dynamic Values
If users need to compute the object at runtime rather than supply a static JSON literal, use an Expression Control instead:
// Static structure known at configuration time → Object Control
staticConfig: i.pins.data({
control: i.controls.object({
label: 'Config',
default: { feature: true },
}),
});
// Structure depends on runtime data → Expression Control
dynamicConfig: i.pins.data({
control: i.controls.expression({
label: 'Config Builder',
placeholder: '({ userId }) => ({ feature: userId !== null })',
}),
});Select Control
Select controls provide a dropdown interface for choosing from a predefined set of options. They support both static option lists and dynamic options that are fetched asynchronously, making them ideal for configuration choices and API-driven selections.
Expression Control
Expression controls provide a powerful JavaScript code editor that allows users to input executable code that is evaluated at runtime. This control is designed for advanced users who need dynamic data transformation, complex calculations, or conditional logic within their workflows.