Suite.js: Development Update

Published on May 22, 2024.

A few weeks ago, I announced on LinkedIn a project that I've been working on called Suite.js. Suite.js is a free JavaScript runtime that makes NetSuite integration easy.

Suite.js is based on an earlier project that I had been working on called Aloe.js. I've been using Aloe.js on client projects for more than a year, and privately beta testing it with a few developer friends for nearly as long. Earlier this year, I made the decision to develop a NetSuite-focused version of Aloe, and thus Suite.js was born.

In this post, I want to give an update on the project, and provide another example of how Suite.js can be used.

Improved Support for SuiteAnalytics Connect

Over the past few weeks, my focus with Suite.js has been on its support for SuiteAnalytics Connect. SuiteAnalytics Connect is NetSuite's ODBC, JDBC, and ADO.NET offering, and Suite.js specifically supports JDBC.

I've had SuiteAnalytics Connect support working in Suite.js for several months now, and some of the speed that I've been seeing from it has been wild.

But more recently, I've been tuning Suite.js's SuiteAnalytics Connect API to get even better performance, to make it more flexible, and to reduce its memory footprint. And over the past few days, I've made a lot of progress in those areas.

One of the client projects that I've been using Suite.js on involves retrieving recent sales orders. With the improved SuiteAnalytics Connect support, I've been able to download the transaction headers and lines for 2,049 orders - and generate a JSON-encoded file for each order - in 45 seconds.

Similarly, I was able to download the headers and lines for 10,629 sales orders in 2 minutes and 38 seconds.

If you're interested in what the code for that app looks like, see "Suite.js / SuiteAnalytics Connect Example" at the end of this post.

Support for Encrypted JavaScript Files

A recent addition that I've made to Suite.js is support for AES-256 encrypted Javascript files. This gives developers the ability to encrypt their JavaScript source files, and distribute the encrypted files to their clients, who can then run them with Suite.js. With encrypted JavaScript files, the source code is never exposed, which protects the developer's intellectual property and their client's credentials.

To encrypt a source file, you run Suite.js, specify the file to be encrypted, add a "-encrypt" command line argument, and then specify a secret key value.

For example:

./suitejs sourcefile.js -encrypt secretkey

Suite.js will generate an encrypted file with the name:
sourcefile.encrypted.js

Before generating an encrypted file, Suite.js loads any included files.

To run an encrypted file, you run Suite.js as you normally would, specify the path to the encrypted file, and also specify the key that was used to encrypt the file.

To continue with the example from above, to run the encrypted file, you'd do this:

./suitejs sourcefile.encrypted.js secretkey

What's Next?

At this point, I still have a few things to wrap up. I'm working on improving Suite.js's support for Amazon S3 integration, and standardizing how Suite.js handles file paths. I'm also working on the documentation, and cleaning up some of the example files that will be available. I'm still hoping to start public beta testing in June.

If you want to learn more about Suite.js, please visit https://suitejs.io.

Also, if you'd like to be notified as things progress, I've setup a Suite.js mailing list, and you can signup for it here: https://suitejs.io/newsletter/


Suite.js / SuiteAnalytics Connect Example


var ns = require( 'netsuite-sac-credentials.js' );
			
var query = <<<TEXTBLOCK
	SELECT
		Transaction.ID AS Transaction,
		Transaction.TranID,
		Transaction.TranDate,
		BUILTIN.DF( Transaction.Status ) AS Status,
		ShipTo.State AS ShipToState,
		ShipTo.Zip AS ShipToZip,
		TransactionLine.Item,
		BUILTIN.DF( TransactionLine.Item ) AS ItemName,
		TransactionLine.Quantity			
	FROM 
		Transaction 
		INNER JOIN TransactionShippingAddress AS ShipTo ON
			( ShipTo.nkey = Transaction.ShippingAddress )	
		INNER JOIN TransactionLine ON
			( TransactionLine.Transaction = Transaction.ID )	
	WHERE 
		( Transaction.Type = 'SalesOrd' ) 
		AND ( Transaction.TranDate > BUILTIN.RELATIVE_RANGES( 'DAGO7', 'START' ) )
		AND ( TransactionLine.Mainline = 'F' )
TEXTBLOCK>>>

var nsSACRequest = {
	libraryPath: ns.libraryPath,
	driverPath: ns.driverPath,
	driverClass: ns.driverClass,
	accountNumber: ns.accountNumber,
	suiteJSLicenseNumber: ns.suiteJSLicenseNumber,
	port: ns.port,
	roleID: ns.roleID,
	username: ns.username,
	password: ns.password,
	query: query,
	maxRows: 0,
	returnRows: false,
	javascript: "processRow( rowCount, row );",
	verbose: false,
	jvmVerbose: false
}

writeln( "Running SuiteQL query..." );
writeln;

var lastTransaction = null;

var transactionLines = new Array();

var transactionsProcessed = 0;

var time1 = performance.now();

var nsSACResponse = nsSACExecute( nsSACRequest );

// Process the last transaction.
saveTransaction();

var elapsedTime = ( performance.now() - time1 ) / 1000;

writeln( "Process complete." );
writeln( "• Transactions Processed: " + transactionsProcessed );
writeln( "• Elapsed Time: " + elapsedTime + " seconds" );
writeln;


function processRow( rowCount, row ) {

	if ( row.transaction != lastTransaction ) {
	
		if ( lastTransaction != null ) {								
			saveTransaction();				
		}
		
		transactionLines = new Array();
		
	}

	transactionLines.push( row );
	
	lastTransaction = row.transaction;

}


function saveTransaction() {

	transactionsProcessed += 1;

	writeln( "Processing Transaction # " + transactionsProcessed + ":" );		

	var transaction = {}	
	transaction.id = transactionLines[0].transaction;
	transaction.tranid = transactionLines[0].tranid;
	transaction.trandate = transactionLines[0].trandate;
	transaction.status = transactionLines[0].status;
	transaction.shiptostate = transactionLines[0].shiptostate;
	transaction.shiptozip = transactionLines[0].shiptozip;
	transaction.lines = new Array();
	
	writeln( "• Transaction ID: " + transaction.id );	
		
	for ( var i = 0; i < transactionLines.length; i++ ) {
	
		var transactionLine = {}
		transactionLine.item = transactionLines[i].item;
		transactionLine.itemname = transactionLines[i].itemname;
		transactionLine.quantity = transactionLines[i].quantity;
		
		transaction.lines.push( transactionLine );
	
	}

	var fileName = "Transaction-" + transaction.id + ".json.txt";
	
	writeln( "• File: " + fileName );	
	
	var fileContent = JSON.stringify( transaction, null, 5 );
						
	var writeStatus = writeFile( fileName, fileContent );			
	
	writeln( "• Saved file locally." );	
	
	writeln();
	
}

About Me

Hello, I'm Tim Dietrich. I develop custom software for businesses that are running on NetSuite, including mobile apps, Web portals, Web APIs, and more.

I'm the developer of several popular NetSuite open source solutions, including the SuiteQL Query Tool, SuiteAPI, and more.

I founded SuiteStep, a NetSuite development studio, to provide custom software and AI solutions - and continue pushing the boundaries of what's possible on the NetSuite platform.

Copyright © 2025 Tim Dietrich.