Week 5 • Module 10

N/runtime Module

Governance monitoring, user context, script parameters, and feature detection

🎯 N/runtime Overview

The N/runtime module provides information about the execution environment. It answers key questions:

👤 Getting Current User

Know who's running your script for assignments, logging, and personalization:

const runtime = require('N/runtime');

const currentUser = runtime.getCurrentUser();

log.audit('User Info', {
    id: currentUser.id,           // Internal ID (e.g., 207)
    name: currentUser.name,       // Display name (e.g., "John Smith")
    email: currentUser.email,     // Email address
    role: currentUser.role,       // Current role ID
    roleId: currentUser.roleId,   // Same as role
    roleCenter: currentUser.roleCenter,  // Role center type
    department: currentUser.department,  // Department ID
    location: currentUser.location,      // Location ID
    subsidiary: currentUser.subsidiary   // Subsidiary ID
});

📧 Example: New Employee Welcome Call

When a new employee is created, automatically create a phone call task assigned to the current user (likely HR).

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
define(['N/runtime', 'N/record'], (runtime, record) => {

    const afterSubmit = (context) => {
        if (context.type !== context.UserEventType.CREATE) return;

        const employee = context.newRecord;
        const firstName = employee.getValue('firstname');
        const lastName = employee.getValue('lastname');
        const phoneNumber = employee.getValue('phone');

        // Create phone call assigned to whoever created the employee
        const phoneCall = record.create({ type: record.Type.PHONE_CALL });

        phoneCall.setValue({
            fieldId: 'assigned',
            value: runtime.getCurrentUser().id  // Assign to current user
        });

        phoneCall.setValue({
            fieldId: 'title',
            value: `Welcome call: ${firstName} ${lastName}`
        });

        phoneCall.setValue({
            fieldId: 'message',
            value: `Welcome call for new employee at ${phoneNumber}.`
        });

        const recordId = phoneCall.save();
        log.debug('Phone Call Created', recordId);
    };

    return { afterSubmit };
});

⚙️ Script Parameters

Script parameters let administrators configure scripts without changing code. Define them in the script record, then read at runtime:

const runtime = require('N/runtime');

const script = runtime.getCurrentScript();

// Read a script parameter (defined in script record)
const recipientId = script.getParameter({
    name: 'custscript_notif_recipient'  // Parameter ID
});

const approvalThreshold = script.getParameter({
    name: 'custscript_approval_limit'
});

log.audit('Parameters', { recipientId, approvalThreshold });

💡 Why Use Parameters?

Instead of hardcoding values like email recipients or thresholds, use script parameters. Administrators can change them in the deployment without touching code.

📧 Example: Configurable Notification Recipient

Send a notification when records are created, but let administrators configure who receives it via script parameter.

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
define(['N/runtime', 'N/email'], (runtime, email) => {

    const afterSubmit = (context) => {
        try {
            if (context.type !== context.UserEventType.CREATE) return;

            // Get recipient from script parameter (not hardcoded!)
            const recipientId = runtime.getCurrentScript().getParameter({
                name: 'custscript_notif_recipient'
            });

            email.send({
                author: runtime.getCurrentUser().id,
                recipients: recipientId,
                subject: `New ${context.newRecord.type} record created!`,
                body: `${runtime.getCurrentUser().name} created a new 
                       ${context.newRecord.type} record for 
                       ${context.newRecord.getValue('entityid')}.`
            });

        } catch (e) {
            log.error({ title: e.name, details: e.message });
        }
    };

    return { afterSubmit };
});

🔓 Feature Detection

Check if a NetSuite feature is enabled before using it. This prevents errors when scripts run in accounts with different configurations:

const runtime = require('N/runtime');

// Check if Opportunities feature is enabled
const opportunitiesEnabled = runtime.isFeatureInEffect({
    feature: 'OPPORTUNITIES'
});

// Check other common features
const multiCurrency = runtime.isFeatureInEffect({ feature: 'MULTICURRENCY' });
const advancedBilling = runtime.isFeatureInEffect({ feature: 'ADVANCEDBILLING' });
const serializedInventory = runtime.isFeatureInEffect({ feature: 'SERIALIZEDINVENTORY' });

🔒 Example: Feature-Gated Record Creation

Only create an Opportunity if the feature is enabled in the account.

require(['N/record', 'N/runtime'], (record, runtime) => {

    const opportunitiesEnabled = runtime.isFeatureInEffect({
        feature: 'OPPORTUNITIES'
    });

    if (opportunitiesEnabled) {
        const newOpport = record.create({
            type: record.Type.OPPORTUNITY
        });

        newOpport.setValue({ fieldId: 'title', value: 'Office chairs hire' });
        newOpport.setValue({ fieldId: 'entity', value: 325 });

        newOpport.setSublistValue({
            sublistId: 'item',
            fieldId: 'item',
            line: 0,
            value: 14
        });

        const recordId = newOpport.save();
        log.debug('Opportunity saved!', recordId);

    } else {
        log.debug('Skipped', 'Opportunities feature is not enabled.');
    }
});

⚠️ Common Feature IDs

OPPORTUNITIES, MULTICURRENCY, MULTISUBSIDIARY, ADVANCEDBILLING, SERIALIZEDINVENTORY, LOTTRACKING, CUSTOMRECORDS. Find more in NetSuite Help under "Feature IDs".

⚡ Governance Monitoring

Governance is NetSuite's way of limiting API calls. Every script has a governance budget—exceed it, and your script stops.

Governance Limits by Script Type

Script TypeUnitsNotes
Client Script1,000Runs in browser
User Event1,000Per record action
Suitelet1,000Per page request
Scheduled Script10,000Can reschedule itself
Map/Reduce (getInputData)10,000Data gathering stage
Map/Reduce (map)1,000Per map invocation
Map/Reduce (reduce)5,000Per reduce invocation
Map/Reduce (summarize)10,000Final summary stage

Monitoring Remaining Units

const runtime = require('N/runtime');

function processRecords() {
    const script = runtime.getCurrentScript();

    // Check before expensive operations
    const remaining = script.getRemainingUsage();
    log.audit('Governance', 'Remaining: ' + remaining);

    if (remaining < 100) {
        log.audit('Governance', 'Low on units, stopping early');
        return; // Exit gracefully before hitting limit
    }

    // Continue processing...
}

Common Operation Costs

OperationUnits
record.load()10
record.save() / record.create()10
record.submitFields()10
search.create().run()10
query.runSuiteQL()10
email.send()10
http.request()10
file.load()10

💡 Key Insight

Most operations cost 10 units. With 1,000 units, you can do ~100 operations. Plan accordingly!

🚀 Optimization Strategies

1. Use Searches Over Multiple Loads

// BAD: 100 units for 10 records
for (let id of recordIds) {
    const rec = record.load({ type: 'customer', id: id }); // 10 units each
}

// GOOD: 10 units for all records
const customerSearch = search.create({
    type: 'customer',
    filters: [['internalid', 'anyof', recordIds]],
    columns: ['companyname', 'email']
}).run(); // 10 units total

2. Use submitFields for Updates

// BAD: 20 units (load + save)
const rec = record.load({ type: 'customer', id: 123 });
rec.setValue('email', 'new@example.com');
rec.save();

// GOOD: 10 units
record.submitFields({
    type: 'customer',
    id: 123,
    values: { email: 'new@example.com' }
});

3. Choose Map/Reduce for Large Jobs

Map/Reduce scripts get fresh governance at each stage and can process unlimited records across multiple invocations.

✅ Map/Reduce Advantage

Each stage gets its own governance pool. If map runs out, it yields and resumes later with fresh units.

🌐 Environment Detection

Detect which environment (production vs sandbox) you're running in:

const runtime = require('N/runtime');

// Get account ID
const accountId = runtime.accountId;  // e.g., "1234567" or "1234567_SB1"

// Check if sandbox
const isSandbox = accountId.toLowerCase().includes('_sb');

// Get environment type
const envType = runtime.envType;  // PRODUCTION, SANDBOX, BETA, INTERNAL

if (runtime.envType === runtime.EnvType.SANDBOX) {
    log.debug('Environment', 'Running in Sandbox');
}

📖 Finding This in the Docs

To look up N/runtime:

  1. Go to docs.oracle.com → SuiteScript 2.x Modules
  2. Click on N/runtime Module
  3. Review User, Script, and Session objects

Key pages to bookmark:

🎯 Key Takeaways