Sign up to receive SuiteScript examples and advice directly in your email inbox.

2 SuiteScript 2.0 Modules

Created: May 8, 2017

This is the first article in a series focused on transitioning from SuiteScript 1.0 to SuiteScript 2.0. We kick off this series by introducing the fundamental concept that drives all of SuiteScript 2.0: the "Module".

SuiteScript 2.0 is entirely based on a concept called "modules". These modules give us the capability to structure and organize our code into small, useful individual units that can then be combined together to form larger, more powerful applications.

Previously, the entire SuiteScript 1.0 API was essentially organized as a single global library file. That file got loaded for every single script, regardless how much of the API your script actually utilized.

SS2 Module progression

In SuiteScript 2.0, the APIs have been categorized and separated into modules.

Related API functions have been placed into a module together; for example, all of the search-related APIs have been placed into a module called N/search. The N is a "namespace" that by convention indicates this module is provided by NetSuite.

At this time, there are almost three dozen modules provided by NetSuite for the SuiteScript API. You can find the complete list of NetSuite modules in the Help documents on the page titled "SuiteScript 2.0 Modules".

Now, our scripts only need to load the specific modules they will actually utilize. This prevents our scripts from loading a ton of wasted functionality that they will never use anyway. We'll look at an example of this shortly, but first, let's discuss why this modular architecture is more advantageous for us.

Why Modules?

By moving to a modular architecture, we can now break down our business logic into digestible pieces and build our applications out of these small, reusable pieces. Just like we use functions to break down individual behaviours, we use modules to group and organize related functions. Multiple script entry points can then leverage the modules we've created so that the business logic stays in one single place in our code. This is a big step in helping us keep our code DRY (Don't Repeat Yourself).

The other advantage this modular approach provides for us is automatic dependency management. Our scripts specify exactly which modules they need, and NetSuite will automatically go fetch them for us when our script executes. If the modules we've specified in turn have their own dependencies, NetSuite will automatically handle retrieving those nested dependencies as well.

Compare that to SuiteScript 1.0, where the developer was responsible for explicitly specifying all direct and indirect dependencies, and keeping them in the correct order on the Script record. This was a nightmare to manage for large applications, but with SuiteScript 2.0, it is no longer an issue because all of that is handled automatically for us.

AMD Specification

The JavaScript development community has actually designed several different specifications for defining modules. Each one has its own unique syntax and capabilities. For SuiteScript 2.0, NetSuite has chosen the AMD specification, or Asynchronous Module Definition.

This specification is implemented by the third-party library called require.js. NetSuite has created a customized version of require.js specific to SuiteScript. I won't be going into the specifics of require.js here as the linked resources have plenty of study material if you're curious.

Enough with the theory; let's look at how to actually write one of these modules.

The 2.0 Module Syntax

Every module is going to start out with a JSDoc comment that tells NetSuite what version of SuiteScript we're using, what type of script we're creating, and the accessibility rules for our module.

Here is our JSDoc comment:

/**
 @NApiVersion 2.0
 @NScriptType ClientScript
 @NModuleScope SameAccount
 */

As you can see, we put a few special tags into the module's JSDoc so that NetSuite can identify this script correctly. When we upload our source file to the File Cabinet, NetSuite will use this information to automatically create a Script record for us. We can also add other standard JSDoc tags and descriptions to our comment, but these are the ones that will be leveraged by NetSuite.

Next, every module is declared by invoking the define function from require.js.

/**
 @NApiVersion 2.0
 @NScriptType ClientScript
 @NModuleScope SameAccount
 */
define([], function () {
});

define takes two arguments: the list of dependencies and a callback function to execute once all dependencies have been loaded. For now, we won't use any other modules, so we'll leave the dependency list empty.

We'll use our classic example of a Client Script that displays an alert on pageInit.

We start by adding functions and properties to our module that define our business logic. In this case, I just need one function to display an alert:

/**
 @NApiVersion 2.0
 @NScriptType ClientScript
 @NModuleScope SameAccount
 */
define([], function () {
  function showMessage () {
    alert("Hello, Eric!");
  }
});

Notice there is nothing different or special about the way we write functions within our modules. In the body of our module, we simply write typical JavaScript.

In SuiteScript 1.0, in order to execute this function on the pageInit event, we would have to:

  1. Upload our file
  2. Manually create our Script record
  3. Manually type showMessage into the pageInit Function box

That is not how SuiteScript 2.0 works.

Instead of manually describing which functions the Script should call on specific events, we define that directly inside our module using the return of our callback function.

/**
 @NApiVersion 2.0
 @NScriptType ClientScript
 @NModuleScope SameAccount
 */
define([], function () {
  function showMessage () {
    alert("Hello, Eric!");
  }

  return {
    pageInit: showMessage
  };
});

The keys of our output object are the event IDs, and the values are the functions to call on that event. Each script type has a dedicated page in the Help that defines its events and their IDs.

At this point, we have a complete SuiteScript 2.0 module that defines a Client Script! It's time to upload it to NetSuite and test it out.

Testing the Module

  1. In NetSuite, navigate to Customization > Scripting > Scripts > New.
  2. Click the + icon and select your source file
  3. NetSuite automatically starts a Script record for us based on our JSDoc tags
  4. Name and deploy your script record, just like you would in SuiteScript 1.0
  5. Save the Script record

Notice after you save, the presence of our pageInit function has been automatically detected. We don't have to tell NetSuite the name of the function, and we no longer have to worry about keeping the Script record in sync with the source file if we ever decide to change the name of our function.

Edit a Customer record (or whichever Record Type you deployed your Client Script to), and you should see your alert displayed.

Alright, we have our Client Script displaying a message, but we haven't seen how the dependency management works. Let's look at that now.

Importing NetSuite Modules as Dependencies

For SuiteScript 2.0, NetSuite has created a couple of modules that help us add UI components to our applications. One such module is called N/ui/message, which helps us display native status messages at the top of the record form. I want to replace our simple alert with a native message, so I'm going to leverage this N/ui/message module.

/**
 @NApiVersion 2.0
 @NScriptType ClientScript
 @NModuleScope SameAccount
 */
define(["N/ui/message"], function (message) {
  function showMessage () {
    alert("Hello, Eric!");
  }

  return {
    pageInit: showMessage
  };
});

I've added the module name to our dependency list, and then I've added a corresponding parameter to my callback function. I can name this parameter whatever I wish; I have chosen to call it message. This is how I will reference the module's functionality within my own module.

The N/ui/message module defines two methods of interest: one for creating an instance of a message (create), and one for actually showing the message (show).

We'll replace our previous alert with calls to these two methods:

/**
 @NApiVersion 2.0
 @NScriptType ClientScript
 @NModuleScope SameAccount
 */
define(["N/ui/message"], function (message) {
  function showMessage () {
    message.create({
      title: "Hello, Eric!",
      message: "You've used a dependency correctly.",
      type: message.Type.CONFIRMATION
    }).show();
  }

  return {
    pageInit: showMessage
  };
});

That's all we need to change! Let's upload our changes and see the message module in action. We edit our source file on our Script record just like we would with SuiteScript 1.0.

Refresh your Customer record in Edit mode, and you should now see a nice native message.

SS2 Native Message

There is our new message in all of its native glory.

Major Changes in 2.0

Some very important things to notice here that are stark differences from SuiteScript 1.0. Many of these we will explore in detail in future articles, but I believe it's worth at least mentioning them here.

  1. The create method accepts a single Object parameter rather than a list of separate parameters. This is true for all 2.0 methods. This makes it so that you don't have to remember the specific order of parameters, and specifying each option by name improves the readability for other developers.
  2. Notice that I was able to chain the create and show methods together. This is called a "fluent API", and nearly all SuiteScript 2.0 objects support it.
  3. Lastly, notice the enumeration that we use for the type option. Instead of using raw String IDs everywhere for things like Record Types, Search types, and more, SuiteScript 2.0 has created enumerations instead. In my opinion, this is a welcome relief from using magic strings everywhere and replacing them with programmatic Object references.
  4. Modules have replaced unstructured files
  5. There is not a 1:1 relationship between functions in 1.0 and 2.0. Some 1.0 functions have been broken up into multiple 2.0 functions, 2.0 adds some additional functionality, and some 1.0 functions don't even appear in 2.0.
  6. Sublist indices now start at 0 instead of 1.
  7. Client Script recalc event has been replaced by sublistChanged event.
  8. A new "Map/Reduce" script type has been introduced to handle bulk processing. This script can be run on a schedule, automatically leverages multiple queues and is supposed to manage its own governance usage.
  9. SuiteScript 2.0 is supposed to perform faster. I do not have any metrics to definitively say either way, but NetSuite has stated that SuiteScript 2.0 performs faster than 1.0.

Cohabitation Rules

As long as 1.0 is still supported, there are some rules governing the cohabitation of the two script versions:

  1. 1.0 and 2.0 cannot intermix within the same script
    • You cannot use 2.0 modules as 1.0 Library Scripts
    • You cannot include 1.0 files as 2.0 dependencies
  2. The two versions can intermix in the same account, in the same application, and can even be deployed on the same record.

Conclusion

The module is the foundation of SuiteScript 2.0; not only can we leverage NetSuite's provided modules, but we can create our own custom modules as well. We'll explore this later in the series.