The N/record module is the backbone of SuiteScript 2.1 development. Whether you are creating a new Sales Order, loading an existing Customer record, or running a mass update across hundreds of Items, you will use N/record for all of it. This guide covers everything you need to know with real, production-ready code examples.
What is the N/record Module?
The N/record module in SuiteScript 2.1 provides a programmatic interface to NetSuite record types. It replaces the older SuiteScript 1.0 nlapiLoadRecord and nlapiCreateRecord functions with a cleaner, promise-friendly API that supports both standard and dynamic record access.
How to Load a Record in SuiteScript 2.1
Loading a record retrieves it from NetSuite by record type and internal ID. By default, records are loaded in standard mode which is faster and uses fewer governance units. Use isDynamic: true only when you need to simulate UI field interactions or work with sublist line selections.
/**
* @NApiVersion 2.1
* @NScriptType Suitelet
*/
define(['N/record'], (record) => {
const onRequest = (context) => {
const customerRec = record.load({
type: record.Type.CUSTOMER,
id: 1234,
isDynamic: false
});
const companyName = customerRec.getValue({ fieldId: 'companyname' });
const email = customerRec.getValue({ fieldId: 'email' });
log.debug('Customer', companyName + ' | ' + email);
};
return { onRequest };
});
How to Create a New Record
Creating a record initializes a new in-memory record object. You then set field values and call save() to persist it to the database. The save() method returns the new record’s internal ID.
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/record'], (record) => {
const execute = (context) => {
const journalRec = record.create({
type: record.Type.JOURNAL_ENTRY,
isDynamic: true
});
journalRec.setValue({ fieldId: 'subsidiary', value: 1 });
journalRec.setValue({ fieldId: 'memo', value: 'Auto-generated entry' });
journalRec.selectNewLine({ sublistId: 'line' });
journalRec.setCurrentSublistValue({ sublistId: 'line', fieldId: 'account', value: 101 });
journalRec.setCurrentSublistValue({ sublistId: 'line', fieldId: 'debit', value: 500 });
journalRec.commitLine({ sublistId: 'line' });
const newId = journalRec.save({ enableSourcing: true });
log.debug('Created Journal Entry ID', newId);
};
return { execute };
});
How to Update Fields Without Loading the Full Record
For updating just a few fields on an existing record, record.submitFields() is dramatically more efficient than load, edit, and save. It makes a targeted update in a single governance-light API call and should be your default approach for field-level updates.
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/record'], (record) => {
const afterSubmit = (context) => {
record.submitFields({
type: record.Type.VENDOR,
id: 567,
values: {
'comments': 'Updated by automation on ' + new Date().toISOString()
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
};
return { afterSubmit };
});
How to Delete a Record
Use record.delete() to permanently remove a record. This action is irreversible, so always pair it with a confirmation check or restrict it to admin-level scripts. Many standard record types in NetSuite cannot be deleted if they have dependent transactions.
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
define(['N/record', 'N/search'], (record, search) => {
// Step 1: getInputData β provide the list of records to delete
// Here we search for all Notes linked to a specific entity
const getInputData = () => {
return search.create({
type: search.Type.NOTE,
filters: [['entity', 'anyof', '123']],
columns: ['internalid']
});
};
// Step 2: map β called once per search result; delete each record
const map = (context) => {
const recordId = JSON.parse(context.value).id;
record.delete({ type: record.Type.NOTE, id: recordId });
log.audit('Deleted Note', 'ID: ' + recordId);
};
// Step 3: summarize β log any errors that occurred during map
const summarize = (summary) => {
summary.mapSummary.errors.iterator().each((key, error) => {
log.error('Map error for key ' + key, error);
return true;
});
log.audit('Delete complete', 'Total processed: ' + summary.mapSummary.keys.count);
};
return { getInputData, map, summarize };
});
Standard Mode vs Dynamic Mode
Standard mode (isDynamic: false) should be used for most automation scripts because it is faster and uses less governance. Dynamic mode (isDynamic: true) is required when using selectNewLine and commitLine on sublists, or when NetSuite field sourcing must fire such as when setting an Item on a Sales Order line to auto-populate its Rate.
Governance Costs for N/record Operations
SuiteScript 2.1 enforces governance limits per script execution. record.load() costs 10 governance units, record.create() plus save() costs 20 units, record.submitFields() costs 10 units, and record.delete() costs 20 units. Scheduled Scripts receive 10,000 total governance units while User Event scripts receive 1,000 units per trigger.
Common Errors and How to Fix Them
The three most frequent N/record errors are RCRD_DSNT_EXIST which means the record ID does not exist so always validate your ID before loading, INVALID_FLD_VALUE which means you passed a string where a numeric internal ID was expected, and YOU_DO_NOT_HAVE_PERMISSIONS which means the script execution role lacks access to the record type and you should check the deployment role in Setup.
Next Steps
Now that you have mastered the N/record module fundamentals, explore how to combine it with the N/search module for bulk processing workflows, or check out the SuiteScript 2.1 Map/Reduce script guide for processing thousands of records efficiently within governance limits.
Discover more from The NetSuite Pro
Subscribe to get the latest posts sent to your email.
Leave a Reply