Expand your SuiteScript skills in the Sustainable SuiteScript community for NetSuite developers.

SuiteScript Comparison - Creating NetSuite Records in a Map/Reduce Script

Created: April 8, 2025

NetSuite's documentation team maintains a repository of SuiteScript examples which contains a ton of useful information with code samples.

In the Sustainable SuiteScript Slack community, I've gradually been going through these examples and publishing my own version of each one in what I'm calling "Side-by-Sides".

This article walks through my side-by-side comparison of the mr-create-record example.

The Overview

NetSuite summarizes the purpose of this example like so:

This customization project shows how to create a new sales order record based on custom record workbook data. The workbook stores custom records. The script retrieves the workbook data using the N/query module to use the custom record for a new standard sales order. The script will set the values for each corresponding field and save the order. Finally, the script logs usage, time elapsed, and any possible errors that may occur.

  • NetSuite's code is here.
  • My code is here.

The Comparison

Stages of a Map/Reduce

The Map/Reduce script is unique within SuiteScript in its progressive architecture of entry points: The output of one entry point becomes the input to the next.

Beyond the successive chaining of the entry points, there is a lot of flexibility in how we can utilize these entry points. That flexibility, though, often leads to confusion or inconsistency in how we use these stages.

Here is my standard usage of each stage of Map/Reduce scripts:

  1. getInputData is for gathering all of the data necessary for processing. Typically, this means returning a single Search or Query object, but it might involve consolidating data from several sources. This stage is required by NetSuite.
  2. map is for normalizing all that data. I use this stage to reformat and restructure the data. Primarily that means grouping results and ensuring each value is the expected datatype (e.g. turning strings into numbers where appropriate). I do not always use this stage, as grouping/reformatting are not always required.
  3. reduce is for processing the data, performing whatever business action we are trying to solve with this script (e.g. creating/updating records). I always use a reduce stage.
  4. summarize is for post-processing tasks (e.g. logging metrics, sending email notifications, handling errors). This stage is optional, but I nearly always use one; at the least, I log the errors encountered in previous stages.

Fluent API in SuiteScript

The SuiteScript API is designed such that we often repeatedly call the same method:

    salesOrder.setValue({
  fieldId: 'entity',
  value: queryData.values[0]
})
salesOrder.setValue({
  fieldId: 'memo',
  value: 'Memo field set.'
})
salesOrder.setValue({
  fieldId: 'subsidiary',
  value: queryData.values[1]
})

However, most of these methods are designed as a Fluent interface, which enables us to chain these repeated calls together and reduce a little repetition:

    order.setValue({ fieldId: 'entity', value: itemLines[0]?.entity })
  .setValue({ fieldId: 'memo', value: itemLines[0]?.memo })
  .setValue({ fieldId: 'subsidiary', value: itemLines[0]?.subsidiary })

Where possible, I nearly always take advantage of the Fluent design.

Dynamic versus Standard Mode

NetSuite's Record API provides two different modes for interacting with Record instances:

  • Standard Mode - the default mode; changes to the record are not validated until the record is saved
  • Dynamic Mode - changes to the record are validated immediately on field change

In my long NetSuite career, it has been exceedingly rare that I've needed to explicitly use Dynamic mode; certainly less than five times across all my projects. However, it seems fairly common in examples or questions I see for others to be using Dynamic mode.

I prefer Standard mode because:

  • I do not have to worry about the order in which I set fields,
  • I only have to worry about error-handling at the record level, not the field level, and
  • I find the Standard API, particularly for sublists, more concise.

This is only a personal preference, though. There's nothing inherently wrong with Dynamic mode, so long as you understand the implications of working in the two modes.

Unclear Benefit of Async

I removed all asynchronous code from my version. The original code makes use of async-await functionality, but I do not understand why. It is unclear to me what the benefits are in this particular use case.

That isn't to say there aren't benefits, only to say that I do not understand what they are in this instance.

If you have an explanation and could help me understand, I'd love to learn from you! Hop on the mailing list and send me a reply any time.