Record
Documentation for the NocoDBRecord object in NocoDB Scripts
The NocoDBRecord
object represents a single row of data in a NocoDB table and provides methods to access and work with the cell values within that record.
Overview
Records are typically retrieved from tables or views using query methods like selectRecordsAsync()
or selectRecordAsync()
. Each NocoDBRecord object provides access to:
- The record's unique identifier
- The record's primary display value (name)
- All cell values in the record
- Formatted string representations of cell values
Working with NocoDBRecord objects is central to most NocoDB scripts, as they allow you to read, manipulate, and interpret the data in your base with rich type support.
Properties
Property | Type | Description |
---|---|---|
id | string | The unique identifier for this record |
name | string | The primary display value for this record (derived from display field) |
Methods
getCellValue
Retrieves the value of a specific cell in the record.
Parameters:
fieldOrFieldIdOrName
(Field | string
): The field, field ID, or field name to get the value for
Returns: The value of the cell in a format determined by the field type (see Cell Value Types section)
Throws: Error if the field is not found or not included in the query
Example:
// Get the Projects table
const projectsTable = base.getTable('Projects');
// Query a specific record
const projectRecord = await projectsTable.selectRecordAsync('rec123456');
if (projectRecord) {
// Get cell values using field names
const projectName = projectRecord.getCellValue('Project Name');
const dueDate = projectRecord.getCellValue('Due Date');
const status = projectRecord.getCellValue('Status');
output.text(`Project: ${projectName}`);
output.text(`Due Date: ${dueDate}`);
output.text(`Status: ${status}`);
// Get a cell value using a field object
const assignedToField = projectsTable.getField('Assigned To');
const assignedTo = projectRecord.getCellValue(assignedToField);
if (assignedTo) {
output.text(`Assigned To: ${assignedTo.name}`);
} else {
output.text('This project is unassigned.');
}
}
getCellValueAsString
Gets a cell value formatted as a string, applying appropriate formatting based on the field type.
Parameters:
fieldOrFieldIdOrName
(Field | string
): The field, field ID, or field name to get the value for
Returns: string
- A string representation of the cell value with proper field-type formatting
Throws: Error if the field is not found or not included in the query
Note: This method provides comprehensive formatting for all field types including currency, dates, durations, percentages, and more.
Example:
// Get the Invoices table
const invoicesTable = base.getTable('Invoices');
// Query a specific record
const invoiceRecord = await invoicesTable.selectRecordAsync('rec123456');
if (invoiceRecord) {
// Get formatted string values
const invoiceNumber = invoiceRecord.getCellValueAsString('Invoice Number');
const amount = invoiceRecord.getCellValueAsString('Amount'); // Formatted as currency
const date = invoiceRecord.getCellValueAsString('Date'); // Formatted as date
const isPaid = invoiceRecord.getCellValueAsString('Paid'); // "Checked" or "Unchecked"
const attachments = invoiceRecord.getCellValueAsString('Attachments'); // Comma-separated file names
// Output the formatted values
output.text(`Invoice: ${invoiceNumber}`);
output.text(`Amount: ${amount}`);
output.text(`Date: ${date}`);
output.text(`Paid: ${isPaid}`);
output.text(`Attachments: ${attachments}`);
}
Cell Value Types
Different field types return values in specific formats when using getCellValue()
. Here's a reference for the most common field types:
Text Fields
// Single Line Text & Long Text
const title = record.getCellValue('Title'); // string or null
Number Fields
// Number, Decimal, Currency, Percent
const amount = record.getCellValue('Amount'); // number or null
const quantity = record.getCellValue('Quantity'); // number or null
const price = record.getCellValue('Price'); // number or null
const discount = record.getCellValue('Discount'); // number or null (without % sign)
Boolean Fields
// Checkbox
const isCompleted = record.getCellValue('Completed'); // boolean or null
Date and Time Fields
// Date
const dueDate = record.getCellValue('Due Date'); // string in ISO format or null
// e.g., "2023-08-15"
// DateTime
const createdAt = record.getCellValue('Created At'); // string in ISO format or null
// e.g., "2023-08-15T14:30:00.000Z"
// Time
const startTime = record.getCellValue('Start Time'); // string in ISO format or null
// e.g., "14:30:00.000"
Selection Fields
// Single Select
const status = record.getCellValue('Status'); // string or null
// e.g., "In Progress"
// Multi Select
const tags = record.getCellValue('Tags'); // array of strings or empty array
// e.g., ["Urgent", "Bug"]
Relationship Fields
// LinkToAnotherRecord (one to one / belongs to relationship)
const project = record.getCellValue('Project'); // NocoDBRecord object or null
if (project) {
output.text(`Project ID: ${project.id}`);
output.text(`Project Name: ${project.name}`);
}
// LinkToAnotherRecord (many-to-many / has-many relationship)
const team = record.getCellValue('Team Members'); // RecordQueryResult object or null
while (team.hasMoreRecords) {
await team.loadMoreRecords();
}
output.text(`Team members: ${team.records.length}`);
// Links (new field type for relationships)
const linkedProjects = record.getCellValue('Related Projects'); // Can return count or records
// For many-to-many/has-many: returns RecordQueryResult object or null
// For belongs-to/one-to-one: returns NocoDBRecord object or null
Attachment Field
// Attachment
const documents = record.getCellValue('Documents'); // Array of attachment objects or empty array
/*
Each attachment object has:
{
url: string,
title: string,
mimetype: string,
size: number,
signedUrl?: string,
thumbnails?: {
tiny: { signedUrl: string },
small: { signedUrl: string },
card_cover: { signedUrl: string }
}
}
*/
if (documents && documents.length > 0) {
for (const doc of documents) {
output.text(`File: ${doc.title}`);
output.text(`Type: ${doc.mimetype}`);
output.text(`Size: ${doc.size} bytes`);
output.text(`URL: ${doc.url}`);
}
}
User Field
// User (single)
const assignee = record.getCellValue('Assigned To'); // Collaborator object or null
if (assignee) {
output.text(`Assignee ID: ${assignee.id}`);
output.text(`Assignee Name: ${assignee.name}`);
output.text(`Assignee Email: ${assignee.email}`);
}
// User (multiple)
const reviewers = record.getCellValue('Reviewers'); // Array of Collaborator objects or empty array
if (reviewers && reviewers.length > 0) {
for (const reviewer of reviewers) {
output.text(`Reviewer ID: ${reviewer.id}`);
output.text(`Reviewer Name: ${reviewer.name}`);
output.text(`Reviewer Email: ${reviewer.email}`);
}
}
Formula Field
// Formula fields return a value of the appropriate type based on the formula's output type
const fullName = record.getCellValue('Full Name'); // string or null
const age = record.getCellValue('Age'); // number or null
const isAdult = record.getCellValue('Is Adult'); // boolean or null
Lookup Field
// Lookup fields return values of the type that is being looked up
const managerName = record.getCellValue('Manager Name'); // string or null
const departmentSize = record.getCellValue('Department Size'); // number or null
// Lookup to multiple values
const teamEmails = record.getCellValue('Team Emails'); // Array of values or empty array
Rollup Field
// Rollup fields return values based on the rollup function and field type
const totalHours = record.getCellValue('Total Hours'); // number or null
const averageScore = record.getCellValue('Average Score'); // number or null
const taskCount = record.getCellValue('Task Count'); // number or null
Working with Records
Reading Record Data
// Get the Tasks table
const tasksTable = base.getTable('Tasks');
// Get a specific record
const taskRecord = await tasksTable.selectRecordAsync('rec123456', {
fields: ['Task Name', 'Description', 'Status', 'Due Date', 'Assigned To', 'Priority']
});
if (taskRecord) {
// Read basic properties
output.text(`Record ID: ${taskRecord.id}`);
output.text(`Primary Value: ${taskRecord.name}`);
// Read values using getCellValue
const taskName = taskRecord.getCellValue('Task Name');
const description = taskRecord.getCellValue('Description');
const status = taskRecord.getCellValue('Status');
const dueDate = taskRecord.getCellValue('Due Date');
const assignedTo = taskRecord.getCellValue('Assigned To');
const priority = taskRecord.getCellValue('Priority');
// Display the task information
output.markdown(`# Task: ${taskName}`);
output.text(`Description: ${description || 'No description provided'}`);
output.text(`Status: ${status || 'Not set'}`);
output.text(`Due Date: ${dueDate ? new Date(dueDate).toLocaleDateString() : 'No due date'}`);
if (assignedTo) {
output.text(`Assigned To: ${assignedTo.name} (${assignedTo.email})`);
} else {
output.text('This task is unassigned.');
}
output.text(`Priority: ${priority || 'Not set'}`);
}
Comparing Records
// Get the Employees table
const employeesTable = base.getTable('Employees');
// Get two employee records
const employee1 = await employeesTable.selectRecordAsync('rec123456');
const employee2 = await employeesTable.selectRecordAsync('rec789012');
if (employee1 && employee2) {
// Compare salary values
const salary1 = employee1.getCellValue('Salary') || 0;
const salary2 = employee2.getCellValue('Salary') || 0;
// Compare departments
const dept1 = employee1.getCellValue('Department');
const dept2 = employee2.getCellValue('Department');
// Compare hire dates
const hireDate1 = employee1.getCellValue('Hire Date');
const hireDate2 = employee2.getCellValue('Hire Date');
// Output comparison
output.markdown(`# Employee Comparison`);
output.markdown(`## ${employee1.getCellValue('Name')} vs. ${employee2.getCellValue('Name')}`);
output.text(`Salary: $${salary1.toLocaleString()} vs. $${salary2.toLocaleString()}`);
output.text(`Department: ${dept1} vs. ${dept2}`);
if (hireDate1 && hireDate2) {
const date1 = new Date(hireDate1);
const date2 = new Date(hireDate2);
const seniorityDiff = Math.floor((date2 - date1) / (1000 * 60 * 60 * 24));
if (seniorityDiff > 0) {
output.text(`${employee1.getCellValue('Name')} has been with the company ${seniorityDiff} days longer.`);
} else if (seniorityDiff < 0) {
output.text(`${employee2.getCellValue('Name')} has been with the company ${Math.abs(seniorityDiff)} days longer.`);
} else {
output.text('Both employees were hired on the same day.');
}
}
}
Working with Linked Records
// Get the Projects and Tasks tables
const projectsTable = base.getTable('Projects');
const tasksTable = base.getTable('Tasks');
// Get a project record with its linked tasks
const projectRecord = await projectsTable.selectRecordAsync('25', {
fields: ['Project Name', 'Description', 'Status', 'Tasks']
});
if (projectRecord) {
const projectName = projectRecord.getCellValue('Project Name');
const tasks = projectRecord.getCellValue('Tasks') || [];
while(tasks.hasMoreRecords) {
await tasks.loadMoreRecords()
}
output.markdown(`# Project: ${projectName}`);
output.text(`Status: ${projectRecord.getCellValue('Status')}`);
output.text(`Number of Tasks: ${tasks.records.length}`);
if (tasks.records.length > 0) {
// Direct access to linked record information
output.markdown('## Task Summary');
for (const task of tasks.records) {
output.text(`- ${task.name}`);
}
// For detailed task information, we need to query each task
output.markdown('## Detailed Task Information');
// Get all task IDs
const taskIds = tasks.recordIds
// Query the tasks with additional fields
const taskRecords = await tasksTable.selectRecordsAsync({
fields: ['Task Name', 'Status', 'Due Date', 'Assigned To'],
recordIds: taskIds
});
// Process each task with full details
for (const taskRecord of taskRecords.records) {
const taskName = taskRecord.getCellValue('Task Name');
const status = taskRecord.getCellValue('Status');
const dueDate = taskRecord.getCellValue('Due Date');
const assignedTo = taskRecord.getCellValue('Assigned To');
output.markdown(`### ${taskName}`);
output.text(`Status: ${status || 'Not set'}`);
output.text(`Due Date: ${dueDate ? new Date(dueDate).toLocaleDateString() : 'No due date'}`);
output.text(`Assigned To: ${assignedTo ? assignedTo.name : 'Unassigned'}`);
}
}
}
Using Record Values in Calculations
// Get the Sales table
const salesTable = base.getTable('Sales');
// Query sales records for a specific period
const salesRecords = await salesTable.selectRecordsAsync({
fields: ['Date', 'Product', 'Quantity', 'Unit Price', 'Total']
});
while(salesRecords.hasMoreRecords) {
await salesRecords.loadMoreRecords()
}
// Perform calculations with record values
let totalSales = 0;
let totalQuantity = 0;
let highestSale = { amount: 0, product: null, date: null };
for (const record of salesRecords.records) {
const quantity = record.getCellValue('Quantity') || 0;
const total = record.getCellValue('Total') || 0;
// Update totals
totalSales += total;
totalQuantity += quantity;
// Check if this is the highest sale
if (total > highestSale.amount) {
highestSale = {
amount: total,
product: record.getCellValue('Product'),
date: record.getCellValue('Date')
};
}
}
// Calculate average sale value
const averageSale = salesRecords.records.length > 0
? totalSales / salesRecords.records.length
: 0;
// Output the results
output.markdown('# Sales Analysis');
output.text(`Total Sales: $${totalSales.toLocaleString()}`);
output.text(`Total Quantity: ${totalQuantity.toLocaleString()} units`);
output.text(`Average Sale: $${averageSale.toFixed(2)}`);
if (highestSale.product) {
output.markdown('## Highest Sale');
output.text(`Product: ${highestSale.product}`);
output.text(`Amount: $${highestSale.amount.toLocaleString()}`);
output.text(`Date: ${highestSale.date ? new Date(highestSale.date).toLocaleDateString() : 'Unknown'}`);
}
Complex Record Processing
// Get the Inventory table
const inventoryTable = base.getTable('Inventory');
// Get all inventory records
const inventoryRecords = await inventoryTable.selectRecordsAsync({
fields: ['Item Name', 'Category', 'Quantity', 'Reorder Level', 'Supplier', 'Price']
});
// Identify items that need to be reordered
const reorderItems = [];
const lowStockItems = [];
const outOfStockItems = [];
for (const record of inventoryRecords.records) {
const itemName = record.getCellValue('Item Name');
const quantity = record.getCellValue('Quantity') || 0;
const reorderLevel = record.getCellValue('Reorder Level') || 0;
// Add to appropriate lists
if (quantity === 0) {
outOfStockItems.push({
id: record.id,
name: itemName,
category: record.getCellValue('Category'),
supplier: record.getCellValue('Supplier'),
price: record.getCellValue('Price')
});
} else if (quantity <= reorderLevel) {
reorderItems.push({
id: record.id,
name: itemName,
category: record.getCellValue('Category'),
currentQuantity: quantity,
reorderLevel: reorderLevel,
supplier: record.getCellValue('Supplier'),
price: record.getCellValue('Price')
});
} else if (quantity <= reorderLevel * 1.2) {
// Items at 120% or less of reorder level are considered low stock
lowStockItems.push({
id: record.id,
name: itemName,
category: record.getCellValue('Category'),
currentQuantity: quantity,
reorderLevel: reorderLevel
});
}
}
// Group reorder items by supplier
const itemsBySupplier = {};
for (const item of reorderItems) {
const supplier = item.supplier || 'Unknown Supplier';
if (!itemsBySupplier[supplier]) {
itemsBySupplier[supplier] = [];
}
itemsBySupplier[supplier].push(item);
}
// Output the inventory report
output.markdown('# Inventory Report');
if (outOfStockItems.length > 0) {
output.markdown('## Out of Stock Items');
output.table(outOfStockItems);
}
if (reorderItems.length > 0) {
output.markdown('## Items to Reorder');
output.table(reorderItems);
output.markdown('## Reorder by Supplier');
for (const [supplier, items] of Object.entries(itemsBySupplier)) {
output.markdown(`### ${supplier}`);
// Calculate order totals
const orderTotal = items.reduce((sum, item) => sum + (item.price || 0), 0);
output.table(items);
output.text(`Order Total: $${orderTotal.toLocaleString()}`);
}
}
if (lowStockItems.length > 0) {
output.markdown('## Low Stock Items');
output.table(lowStockItems);
}
Best Practices
-
Check for null values - Always handle the possibility of null or undefined field values with default values or conditional logic.
-
Use appropriate methods - Use
getCellValue()
when you need the actual value for calculations or logic, andgetCellValueAsString()
for display purposes. -
Handle linked records properly - Remember that linked records from
getCellValue()
only contain minimal information (id and name). For detailed information, query the linked records from their table. -
Field parameter consistency - Prefer using field names (strings) for consistency unless you need special field object functionality.
-
Error handling - Wrap record operations in try-catch blocks to handle possible errors gracefully.
-
Update records with care - When updating records, preserve existing values for fields you don't intend to change.