14 minutes Read

Published On

How to Extract Specific Item Types From nlobjSearchRecord in NetSuite SuiteScript

Key Takeaways

  • nlapiSearchRecord('item', ...) returns generic item type results. The getRecordType() method on each result gives you the actual item type (inventoryitem, noninventoryitem, assemblyitem, etc.) so you can use it with nlapiLoadRecord or nlapiCreateRecord.
  • nlapiLoadRecord('item', id) will not work. You must pass the specific record type. This is the most common error developers hit after a successful search.
  • The recordtype column approach is the most reliable method. Adding new nlobjSearchColumn('recordtype') to your search columns lets you retrieve the item type without calling getRecordType() on each result.
  • SuiteScript 2.0 uses search.Type constants for cleaner type handling. The N/search module approach is the preferred method for new scripts.
  • Always validate item type before loading. Branching logic based on record type prevents failures on mixed-type item lists.

When you search items in NetSuite using nlapiSearchRecord, you pass 'item' as the record type to match across all item subtypes at once. That works for the search. The problem comes when you need to load or create one of those items. nlapiLoadRecord does not accept 'item' as a type. It requires the specific subtype: 'inventoryitem', 'noninventoryitem', 'assemblyitem', 'serviceitem', and so on.

This gap between how you search items and how you load them trips up developers building scripts that need to act on items after retrieving them from a search. This guide covers every way to extract the specific item type from a search result, with working code examples for both SuiteScript 1.0 and 2.0.

Why nlapiSearchRecord Uses ‘item’ But nlapiLoadRecord Does Not

NetSuite’s item search uses 'item' as a universal type that matches all item subtypes. This is intentional. It lets you search across inventory items, non-inventory items, assembly items, and service items in a single query without running separate searches for each type.

The nlapiLoadRecord and nlapiCreateRecord Functions work differently. They operate on specific record types, not on the generic 'item' category. NetSuite has separate record schemas for each item type, and the API needs to know exactly which schema to use when loading or creating a record.

This means any script that searches for items and then loads or modifies them needs to know the specific type of each result before calling nlapiLoadRecord.

Method 1: Using getRecordType() on Each Search Result

The getRecordType() method on the nlobjSearchRecord object returns the specific record type of each result as a string. This is the simplest approach when you are iterating through search results one at a time.

The Code

// SuiteScript 1.0
function extractAndLoadItems() {
    var filters = [
        new nlobjSearchFilter('internalid', null, 'anyof', [11111, 22222, 33333])
    ];
    
    var columns = [
        new nlobjSearchColumn('itemid'),
        new nlobjSearchColumn('displayname')
    ];
    
    var results = nlapiSearchRecord('item', null, filters, columns);
    
    if (!results) {
        nlapiLogExecution('DEBUG', 'No results found');
        return;
    }
    
    for (var i = 0; i < results.length; i++) {
        var result = results[i];
        
        // Extract the specific record type from this result
        var recordType = result.getRecordType();
        var internalId  = result.getId();
        
        nlapiLogExecution('DEBUG', 'Item type: ' + recordType + ' | ID: ' + internalId);
        
        // Now use the specific type to load the record correctly
        var itemRecord = nlapiLoadRecord(recordType, internalId);
        
        // Perform your operation on the loaded record
        itemRecord.setFieldValue('description', 'Updated via script');
        nlapiSubmitRecord(itemRecord);
    }
}

What getRecordType() Returns

getRecordType() returns the NetSuite internal record type string for the item. The values you will encounter most often are:

Item TypegetRecordType() Returns
Inventory Iteminventoryitem
Non-Inventory Itemnoninventoryitem
Assembly / Bill of Materialsassemblyitem
Service Itemserviceitem
Other Charge Itemotherchargeitem
Kit / Package Itemkititem
Download Itemdownloaditem
Gift Certificategiftcertificateitem

Pass the returned string directly into nlapiLoadRecord or nlapiCreateRecord and it will work correctly.

Method 2: Adding the recordtype Column to Your Search

Instead of calling getRecordType() on each result after the fact, you can add 'recordtype' as a search column and retrieve the type value alongside the other data you need. This is useful when you need the type for logic decisions before deciding whether to load the record at all.

The Code

// SuiteScript 1.0 — recordtype as a search column
function searchItemsWithType() {
    var filters = [
        new nlobjSearchFilter('isinactive', null, 'is', 'F')
    ];
    
    var columns = [
        new nlobjSearchColumn('itemid'),
        new nlobjSearchColumn('displayname'),
        new nlobjSearchColumn('recordtype')  // Add this column
    ];
    
    var results = nlapiSearchRecord('item', null, filters, columns);
    
    if (!results) {
        return;
    }
    
    for (var i = 0; i < results.length; i++) {
        var result = results[i];
        
        // Get the record type from the column value
        var recordType = result.getValue('recordtype');
        var internalId  = result.getId();
        
        // Branch logic based on type before loading
        if (recordType === 'inventoryitem' || recordType === 'assemblyitem') {
            // Only load inventory and assembly items for this operation
            var itemRecord = nlapiLoadRecord(recordType, internalId);
            // ... perform operations
        }
    }
}

When to Use the Column Approach vs. getRecordType()

The column approach works better when you need to filter by type before loading, because it lets you make the type decision at the result-processing stage without calling getRecordType() on every item. For simple iteration where you load every result regardless of type, getRecordType() is cleaner.

Method 3: SuiteScript 2.0 Equivalent Using N/search Module

For scripts using SuiteScript 2.0, the approach is similar but uses the N/search module. The result.getValue({ name: 'recordtype' }) call on each search result returns the specific item type.

The Code

/**
 * @NApiVersion 2.x
 * @NScriptType ScheduledScript
 */
define(['N/search', 'N/record', 'N/log'], function(search, record, log) {

    function execute(context) {
        
        var itemSearch = search.create({
            type: search.Type.ITEM,
            filters: [
                search.createFilter({
                    name: 'isinactive',
                    operator: search.Operator.IS,
                    values: ['F']
                })
            ],
            columns: [
                search.createColumn({ name: 'itemid' }),
                search.createColumn({ name: 'displayname' }),
                search.createColumn({ name: 'recordtype' })
            ]
        });
        
        var searchResultSet = itemSearch.run();
        
        // Process results in pages to handle large result sets
        searchResultSet.each(function(result) {
            
            var recordType = result.getValue({ name: 'recordtype' });
            var internalId  = result.id;
            
            log.debug({
                title: 'Processing item',
                details: 'Type: ' + recordType + ' | ID: ' + internalId
            });
            
            // Only process inventory items in this example
            if (recordType === 'inventoryitem') {
                var itemRecord = record.load({
                    type: recordType,
                    id: internalId,
                    isDynamic: false
                });
                
                // Perform your operation
                itemRecord.setValue({
                    fieldId: 'description',
                    value: 'Updated via SS2.0 script'
                });
                
                itemRecord.save();
            }
            
            return true; // Continue iteration
        });
    }

    return {
        execute: execute
    };
});

SuiteScript 2.0 Item Type Constants

The search.Type object provides named constants for item types, which is cleaner than passing raw strings. Use these instead of string literals wherever possible to avoid typos.

search.Type.INVENTORY_ITEM        // 'inventoryitem'
search.Type.NON_INVENTORY_ITEM    // 'noninventoryitem'
search.Type.ASSEMBLY_ITEM         // 'assemblyitem'
search.Type.SERVICE_ITEM          // 'serviceitem'
search.Type.OTHER_CHARGE_ITEM     // 'otherchargeitem'
search.Type.KIT_ITEM              // 'kititem'

Practical Use Case: Updating Items Based on Type

A common real-world use for this technique is running an update across a mixed list of items where the update logic differs by type. Inventory items might need quantity adjustments. Non-inventory items might need a pricing update. Service items might need a rate change. All three are returned by a single 'item' search, but each requires different handling.

// SuiteScript 1.0 — branching update logic by item type
function updateItemsByType(itemIds) {
    
    var filters = [
        new nlobjSearchFilter('internalid', null, 'anyof', itemIds)
    ];
    
    var columns = [
        new nlobjSearchColumn('itemid'),
        new nlobjSearchColumn('recordtype')
    ];
    
    var results = nlapiSearchRecord('item', null, filters, columns);
    
    if (!results) {
        nlapiLogExecution('DEBUG', 'No items found for the provided IDs');
        return;
    }
    
    for (var i = 0; i < results.length; i++) {
        var result    = results[i];
        var type      = result.getRecordType();
        var id        = result.getId();
        var itemName  = result.getValue('itemid');
        
        try {
            var rec = nlapiLoadRecord(type, id);
            
            switch (type) {
                case 'inventoryitem':
                    rec.setFieldValue('reorderpoint', '10');
                    break;
                    
                case 'noninventoryitem':
                    rec.setFieldValue('rate', '150.00');
                    break;
                    
                case 'serviceitem':
                    rec.setFieldValue('rate', '200.00');
                    break;
                    
                default:
                    nlapiLogExecution('DEBUG', 'No update logic for type: ' + type);
                    continue;
            }
            
            nlapiSubmitRecord(rec, true);
            nlapiLogExecution('DEBUG', 'Updated ' + type + ': ' + itemName);
            
        } catch (e) {
            nlapiLogExecution('ERROR', 'Failed on ' + type + ' id=' + id + ': ' + e.message);
        }
    }
}

Common Mistakes and How to Avoid Them

Passing ‘item’ to nlapiLoadRecord

This is the error that sends developers to this guide. nlapiLoadRecord('item', id) throws a runtime error because 'item' is not a valid record type for load operations. Always extract the specific type first.

Not Handling Null Search Results

nlapiSearchRecord returns null when no results match your filters. Always check for null before iterating. Calling .length on null throws an error.

Assuming All Items in a List Are the Same Type

If your item list comes from user input, a saved search, or an integration, do not assume type uniformity. A list that looks like all inventory items may include assembly items or kit items. Always read the type from the result.

Ignoring the 1000-Record Search Limit in SuiteScript 1.0

nlapiSearchRecord returns a maximum of 1000 results. For larger item sets, use nlapiCreateSearch with runSearch() and paginate through results using getResults(start, end). In SuiteScript 2.0, use search.run().each() or search.run().getRange() to handle pagination correctly.

Frequently Asked Questions

Q: Why does nlapiSearchRecord accept ‘item’ but nlapiLoadRecord does not?

nlapiSearchRecord uses 'item' as a cross-type search alias that matches all item subtypes in a single query. nlapiLoadRecord requires the exact record schema identifier because each item type has its own set of fields and behaviors. The generic 'item' type does not map to any single record schema, which is why load operations require a specific type.

Q: What is the difference between getRecordType() and getValue(‘recordtype’)?

getRecordType() is a method on the nlobjSearchRecord object that returns the record type of the current result. getValue('recordtype') retrieves the value of the recordtype field if you included it as a search column. Both return the same string value. getRecordType() does not require adding a column to your search, but getValue('recordtype') lets you access the type before deciding whether to load the record at all.

Q: Does this technique work for other record types beyond items?

Yes. The same approach applies to any NetSuite search where results can span multiple record subtypes. Contacts and entities are common examples. Search with the parent type, then use getRecordType() to get the specific type before loading.

Q: What is the SuiteScript 2.0 equivalent of nlobjSearchRecord.getRecordType()?

In SuiteScript 2.0, the result.recordType property on each search.Result object returns the specific record type. You can also retrieve it as a column value using result.getValue({ name: 'recordtype' }) if you added the recordtype column to your search.

Final Thoughts

The mismatch between how nlapiSearchRecord accepts items and how nlapiLoadRecord requires them is a consistent friction point in NetSuite SuiteScript development. The fix is always the same: extract the specific type from each search result using getRecordType() or the recordtype column, then pass that type to your load or create operation.

For new scripts, the SuiteScript 2.0 approach with N/search and search.Type constants is cleaner and avoids the 1000-result limit through built-in pagination support. For existing 1.0 scripts, the techniques in this guide require no refactoring beyond adding the type extraction step.

Table of Contents

Contact Us

By submitting this form, you agree to our privacy policy and terms of service.

Related resources you might be interested in

We'd love to help you with all your NetSuite needs

Folio3 Your Top Choice:

Middle East Partner 2025
education award 2025
Winner Award
Software and IT Services 2024
Financial-Services-2023
SuiteCommerce 2023

Let's discuss your NetSuite needs

Hello, How can we help you?

Get a 45-Minute
NetSuite Consulting Session

Worth $2,000 for Free

Grab the opportunity to speak with one of our top-rated consultants to get expert guidance on your NetSuite needs.