๐งฉ SuiteScript Security & Governance Best Practices in NetSuite
Introduction
SuiteScript gives developers unmatched power to automate and extend NetSuite, but with great power comes great responsibility โ and governance limits.
NetSuite enforces strict usage governance rules to prevent scripts from consuming excessive system resources. Additionally, every script must follow secure data handling and access control principles to ensure compliance and system stability.
In this tutorial, weโll explore:
โ
Governance unit handling
โ
Script optimization techniques
โ
Secure data access practices
โ
Real examples for User Event, Map/Reduce, and Suitelet scripts
๐ก What Is Script Governance in NetSuite?
Governance controls how much server time and API resources a script can use before timing out.
Every SuiteScript operation consumes โusage units.โ
When your script exceeds its usage limit, youโll hit the common error:
โ ๏ธ โSSS_USAGE_LIMIT_EXCEEDEDโ
Default Usage Limits by Script Type
Script Type | Limit |
---|---|
User Event / Client Script | 1,000 units |
Scheduled Script | 10,000 units |
Map/Reduce | 5,000 (per stage) |
Suitelet | 1,000 units |
Workflow Action | 5,000 units |
RESTlet | 5,000 units |
โ๏ธ How to Monitor and Handle Governance Usage
Step 1: Use runtime.getCurrentScript()
You can track remaining units dynamically.
const script = runtime.getCurrentScript();
log.debug('Remaining Units', script.getRemainingUsage());
Step 2: Use yield()
in Scheduled or Map/Reduce
If youโre running a large dataset, yield execution to avoid hitting limits.
if (runtime.getCurrentScript().getRemainingUsage() < 200) {
runtime.yield();
}
Step 3: Break Processes into Batches
Process large record sets using:
- Search paging (1000 records at a time)
- Map/Reduce data chunking
- Saved Search filters for smaller groups
โ Example:
search.create({ type: 'invoice' })
.runPaged({ pageSize: 500 })
.pageRanges.forEach(range => {
const page = resultSet.fetch({ index: range.index });
// Process each page
});
๐งฑ Security Best Practices
1๏ธโฃ Restrict Script Deployment Audience
Always limit deployment to specific roles, departments, or subsidiaries under the Audience tab.
Avoid:
Deploying scripts to All Roles โ this may expose sensitive logic to unintended users.
2๏ธโฃ Validate User Access in Code
Use runtime.getCurrentUser()
for role-based checks.
const user = runtime.getCurrentUser();
if (user.role !== 3) { // 3 = Administrator
throw error.create({ name: 'ACCESS_DENIED', message: 'Not authorized.' });
}
3๏ธโฃ Never Store Sensitive Data in Plain Text
Use NetSuiteโs Secrets Management or Encrypted Fields for:
- API tokens
- Passwords
- Customer financial data
Modules to use:N/credential
or N/secureKey
for sensitive credentials.
4๏ธโฃ Always Sanitize User Input
For Suitelets and RESTlets:
- Escape all HTML inputs
- Validate query parameters
- Avoid direct execution from URL parameters
Example:
const recordId = parseInt(request.parameters.id, 10);
if (isNaN(recordId)) throw 'Invalid record ID';
5๏ธโฃ Avoid Logging Sensitive Information
Logs are visible to admins; never log:
- Email addresses
- Credit card numbers
- Customer PII
Use sanitized logs:
log.debug('Processing Customer', { id: customerId });
๐ Optimization Techniques for Performance
Area | Optimization Tip |
---|---|
Saved Searches | Filter with indexed fields (internal IDs, dates) |
Record Loads | Use lookupFields() instead of full record.load() |
Loops | Minimize nested loops; use arrays or maps |
APIs | Cache static data in script or runtime |
Logging | Limit excessive log.debug() calls |
Governance | Call getRemainingUsage() often and yield early |
Example:
const data = search.lookupFields({
type: 'customer',
id: 123,
columns: ['email', 'companyname']
});
โ
Saves up to 90% of usage units compared to record.load()
.
๐งฎ Example: Safe and Scalable Map/Reduce
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
define(['N/search', 'N/record', 'N/runtime'], (search, record, runtime) => {
function getInputData() {
return search.create({ type: 'invoice', filters: [['status', 'anyof', 'Open']] });
}
function map(context) {
const data = JSON.parse(context.value);
const invoiceId = data.id;
const remaining = runtime.getCurrentScript().getRemainingUsage();
if (remaining < 200) runtime.yield();
record.submitFields({
type: 'invoice',
id: invoiceId,
values: { memo: 'Updated via Map/Reduce' }
});
}
return { getInputData, map };
});
โ Handles thousands of invoices safely without exceeding limits.
๐ง Governance Recovery Strategies
If your script frequently hits limits:
- Convert Scheduled โ Map/Reduce
- Add checkpoints (
yield()
orsetRecoveryPoint()
) - Use Saved Search Filters to pre-filter records
- Store progress in Custom Records
- Log checkpoints in Execution Logs
๐ Real-World Example: Secure RESTlet Endpoint
/**
* @NApiVersion 2.1
* @NScriptType Restlet
*/
define(['N/record', 'N/runtime'], (record, runtime) => {
const get = (context) => {
const user = runtime.getCurrentUser();
if (user.role !== 3) throw 'Unauthorized access.';
return record.load({ type: 'customer', id: context.id });
};
return { get };
});
โ Only allows access to admin users; prevents misuse of RESTlet endpoints.
๐งฉ Common Errors & Fixes
Error | Cause | Fix |
---|---|---|
SSS_USAGE_LIMIT_EXCEEDED | Too many record loads or searches | Use paging or yield |
SSS_REQUEST_TIME_EXCEEDED | Long-running RESTlet or Suitelet | Break process or optimize query |
INVALID_ROLE | Unauthorized user | Adjust deployment or add role validation |
INVALID_CREDENTIAL | Hardcoded password/token | Move credentials to secure storage |
๐งฐ SuiteScript Audit & Monitoring
Use:
- Execution Logs โ for tracking script runs
- System Notes v2 โ for record change history
- Script Queue Monitor โ to review scheduled scripts
- Script Execution Governance Search โ find overused scripts
Create a Saved Search for governance tracking:
Type = Script Execution
Summary Type = Maximum
Field = Usage Units
๐งฉ Best Practices Summary
โ Security
- Use least privilege (restrict deployment).
- Validate every external request.
- Avoid hardcoded secrets.
โ Governance
- Use efficient APIs (
lookupFields
,search.lookupFields
). - Add yields and recovery points.
- Paginate results with
runPaged()
.
โ Maintenance
- Add logs for checkpoints only.
- Test governance in Sandbox with large datasets.
- Document governance cost of every major script.
๐ Related Tutorials
- ๐ Custom GL Lines Plug-in in NetSuite
- ๐ User Event Scripts for Automation
- ๐ Advanced Approval Workflows in NetSuite
โ FAQ
Q1. What happens when a script exceeds governance limits?
It throws SSS_USAGE_LIMIT_EXCEEDED
, halting execution โ you must yield or split your logic.
Q2. Can governance limits be increased?
No. Theyโre fixed per script type, but you can optimize or use Map/Reduce for higher limits.
Q3. Do governance units reset automatically?
Yes โ they reset at each script execution or when yielding in Map/Reduce.
Q4. How can I test script performance?
Use large datasets in Sandbox and monitor via Execution Logs for unit consumption.
๐งญ Summary
SuiteScript Security and Governance define how efficiently and safely your NetSuite automation runs.
By following these best practices โ limit access, optimize execution, yield early, and secure data โ you ensure scripts scale reliably across large datasets and multi-role environments.
Building with governance in mind is what separates a good NetSuite developer from a great one.
Leave a Reply