Loading linked records in scripts
When you get a linked record field value, you only get an array with the record's name and ID—not the full record. To access fields from linked records, you need to load the record using selectRecordAsync().
The problem
When you retrieve a linked record field value, Airtable returns an array of objects containing only:
name: The primary field valueid: The record ID
You cannot directly access other fields from the linked record.
What doesn't work
let record = await input.recordAsync("Select a record", view)
let spouse = record.getCellValue("Spouse")
// This will NOT work - spouse is just an array with name and id
let spouseNation = spouse.getCellValueAsString("Nation of Birth") // ❌ Error!
Why it fails: spouse is an array like [{name: "Jane Doe", id: "rec123"}], not a record object.
The solution: selectRecordAsync()
To access fields from a linked record, you must load the full record using selectRecordAsync().
Basic pattern
// Get the linked record value (array with name and id)
let linkedRecordValue = record.getCellValue("Spouse")
let linkedRecordId = linkedRecordValue[0].id
// Load the full record
let linkedRecord = await table.selectRecordAsync(linkedRecordId)
// Now you can access fields
let nation = linkedRecord.getCellValueAsString("Nation of Birth") // ✅ Works!
Complete example
Scenario: Accessing spouse information
// Get the table
let table = base.getTable("Characters")
let view = table.getView("Living Males")
// Get a record from user input
let deadMansRecord = await input.recordAsync("Who Died", view)
// Get the linked record value (this is just an array)
let theWidow = deadMansRecord.getCellValue("Spouse")
let theWidowId = theWidow[0].id
// Load the full record
let widowRecord = await table.selectRecordAsync(theWidowId)
// Now access fields from the linked record
let widowHome = widowRecord.getCellValueAsString("Nation of Birth")
console.log(widowHome) // "Germany"
Handling multiple linked records
If a linked record field can contain multiple records, loop through them:
let linkedRecords = record.getCellValue("Team Members")
if (linkedRecords && linkedRecords.length > 0) {
for (let linkedRecord of linkedRecords) {
// Load each full record
let fullRecord = await table.selectRecordAsync(linkedRecord.id)
// Access fields
let email = fullRecord.getCellValueAsString("Email")
console.log(email)
}
}
Loading records from different tables
If the linked record is in a different table, get that table first:
// Current table
let currentTable = base.getTable("Tasks")
// Get linked record from different table
let projectLink = record.getCellValue("Project")
let projectId = projectLink[0].id
// Get the target table
let projectsTable = base.getTable("Projects")
// Load the record from the different table
let projectRecord = await projectsTable.selectRecordAsync(projectId)
// Access fields
let projectName = projectRecord.getCellValueAsString("Name")
let budget = projectRecord.getCellValue("Budget")
Common patterns
Pattern 1: Single linked record
let linkedValue = record.getCellValue("Linked Field")
if (linkedValue && linkedValue.length > 0) {
let linkedRecord = await table.selectRecordAsync(linkedValue[0].id)
let fieldValue = linkedRecord.getCellValue("Field Name")
}
Pattern 2: Multiple linked records
let linkedValues = record.getCellValue("Linked Field")
if (linkedValues && linkedValues.length > 0) {
for (let link of linkedValues) {
let linkedRecord = await table.selectRecordAsync(link.id)
let fieldValue = linkedRecord.getCellValue("Field Name")
// Process each record
}
}
Pattern 3: Chained linked records (lookup of lookup)
// First level
let task = await input.recordAsync("Select task", view)
let projectLink = task.getCellValue("Project")
let projectId = projectLink[0].id
// Load project
let project = await projectsTable.selectRecordAsync(projectId)
// Second level - get client from project
let clientLink = project.getCellValue("Client")
let clientId = clientLink[0].id
// Load client
let client = await clientsTable.selectRecordAsync(clientId)
// Access client fields
let clientEmail = client.getCellValueAsString("Email")
Performance tips
Load only what you need
Specify which fields you need when loading records:
let linkedRecord = await table.selectRecordAsync(recordId, {
fields: ["Name", "Email", "Status"], // Only load these fields
})
This is more efficient than loading all fields.
Batch operations
If you need to load many linked records, consider batching:
let linkedValues = record.getCellValue("Team Members")
let recordIds = linkedValues.map((link) => link.id)
// Load all at once (if supported in your use case)
// Or process in batches to avoid timeouts
Error handling
Always check if linked records exist before accessing them:
let linkedValue = record.getCellValue("Spouse")
if (!linkedValue || linkedValue.length === 0) {
console.log("No linked record found")
return
}
try {
let linkedRecord = await table.selectRecordAsync(linkedValue[0].id)
let value = linkedRecord.getCellValue("Field")
} catch (error) {
console.error("Error loading record:", error)
}
What you get from getCellValue()
When you call getCellValue() on a linked record field, you get:
;[
{
name: "Record Name", // Primary field value
id: "recXXXXXXXXXXXX", // Record ID
},
]
You cannot:
- Access other fields directly
- Call
getCellValue()on this array - Use it as a record object
You must:
- Extract the
id - Use
selectRecordAsync()to load the full record - Then access fields from the loaded record
Common mistakes
Mistake 1: Trying to access fields directly
// ❌ Wrong
let spouse = record.getCellValue("Spouse")
let email = spouse.getCellValue("Email") // Error!
Fix:
// ✅ Correct
let spouse = record.getCellValue("Spouse")
let spouseRecord = await table.selectRecordAsync(spouse[0].id)
let email = spouseRecord.getCellValue("Email")
Mistake 2: Not checking if linked record exists
// ❌ Wrong - will error if no linked record
let spouse = record.getCellValue("Spouse")
let spouseRecord = await table.selectRecordAsync(spouse[0].id)
Fix:
// ✅ Correct
let spouse = record.getCellValue("Spouse")
if (spouse && spouse.length > 0) {
let spouseRecord = await table.selectRecordAsync(spouse[0].id)
}
Mistake 3: Using wrong table
// ❌ Wrong - using current table for different table's record
let projectLink = record.getCellValue("Project")
let projectRecord = await currentTable.selectRecordAsync(projectLink[0].id)
Fix:
// ✅ Correct - get the correct table
let projectsTable = base.getTable("Projects")
let projectLink = record.getCellValue("Project")
let projectRecord = await projectsTable.selectRecordAsync(projectLink[0].id)
Quick reference
| Task | Code |
|---|---|
| Get linked record value | record.getCellValue("Linked Field") |
| Extract record ID | linkedValue[0].id |
| Load full record (same table) | await table.selectRecordAsync(recordId) |
| Load full record (different table) | await otherTable.selectRecordAsync(recordId) |
| Access field from loaded record | loadedRecord.getCellValue("Field") |
| Check if linked record exists | if (linkedValue && linkedValue.length > 0) |
Complete working example
// Setup
let table = base.getTable("Characters")
let view = table.getView("Living Males")
// Get record from user
let record = await input.recordAsync("Select a character", view)
// Get linked spouse
let spouseLink = record.getCellValue("Spouse")
// Check if spouse exists
if (!spouseLink || spouseLink.length === 0) {
output.text("No spouse linked")
} else {
// Load the spouse record
let spouseId = spouseLink[0].id
let spouseRecord = await table.selectRecordAsync(spouseId)
// Access spouse fields
let spouseName = spouseRecord.getCellValueAsString("Name")
let spouseNation = spouseRecord.getCellValueAsString("Nation of Birth")
let spouseAge = spouseRecord.getCellValue("Age")
// Display results
output.text(`Spouse: ${spouseName}`)
output.text(`Nation: ${spouseNation}`)
output.text(`Age: ${spouseAge}`)
}
Tips
- Always use
selectRecordAsync()to load linked records before accessing fields - Check for existence before accessing linked records
- Get the correct table when linked records are in different tables
- Specify fields when loading to improve performance
- Handle errors with try-catch blocks
- Remember: Linked record values are arrays, not record objects
References
Community discussion on loading linked records: 'Loading' a record