Tips > Building Workflows

Transform Data Shapes Between Incompatible Nodes

Different SaaS platforms structure data in incompatible ways.

TipIntermediate2 min read

Different SaaS platforms structure data in incompatible ways. A Code node between them acts as an adapter layer, reshaping the output of one node to match the expected input of another.

Real-world example: Syncing records from Airtable (which returns linked records as arrays of IDs) to Notion (which expects relation properties as arrays of page IDs with specific formatting).

// Mode: Run Once for All Items

// Mapping of Airtable record IDs to Notion page IDs (pre-built or fetched)
const idMap = $input.first().json.idMapping; // { airtableId: notionPageId }

return $input.all().slice(1).map(item => {
  const record = item.json;

  // Airtable returns: { "Assignees": ["recABC", "recDEF"], "Status": "In Progress" }
  // Notion expects: specific property format per type

  return {
    json: {
      parent: { database_id: 'your-notion-database-id' },
      properties: {
        // Title property
        'Task Name': {
          title: [{ text: { content: record['Task Name'] || 'Untitled' } }]
        },
        // Select property
        'Status': {
          select: { name: mapStatus(record['Status']) }
        },
        // Relation property -- map Airtable IDs to Notion page IDs
        'Assignees': {
          relation: (record['Assignees'] || [])
            .map(airtableId => idMap[airtableId])
            .filter(Boolean)
            .map(notionId => ({ id: notionId }))
        },
        // Date property
        'Due Date': {
          date: record['Due Date']
            ? { start: record['Due Date'], end: null }
            : null
        },
        // Number property
        'Story Points': {
          number: parseInt(record['Story Points'], 10) || null
        },
      }
    }
  };
});

function mapStatus(airtableStatus) {
  const statusMap = {
    'To Do': 'Not started',
    'In Progress': 'In progress',
    'Done': 'Complete',
    'Blocked': 'Blocked',
  };
  return statusMap[airtableStatus] || 'Not started';
}
```text
This pattern is reusable for any SaaS-to-SaaS sync. Keep the mapping logic centralized in one Code node to make future field changes easy.

**Related:** [Use Edit Fields in "Map Each" Mode for Simple Renames](../data-transformation/01-use-edit-fields-in-map-each-mode-for-simple-renames.md) | [Configure Payload Size and Binary Data Mode for Large Files](../performance-and-large-files/01-configure-payload-size-and-binary-data-mode-for-large-files.md)

Want this running in your stack?

I build production n8n and Cloudflare automation for teams — the same engineering behind HarperFlow. Fixed-price, escrow-protected, US-based.