SuiteScript 1.0 to 2.1 API Mapping: nlapi Functions to 2.x Modules
This table maps the nlapi functions and nlobj objects you find in legacy SuiteScript 1.0 code to the SuiteScript 2.x module and method that replaces them. Use it while rewriting a 1.0 script as 2.1, or while reading old code to understand what it does.
| SuiteScript 1.0 | SuiteScript 2.x | Module | Governance (2.x units) | Gotcha |
|---|---|---|---|---|
nlapiLoadRecord | record.load({type, id, isDynamic}) | N/record | 10 transaction, 2 custom record, 5 other | Defaults to standard mode. Pass isDynamic: true if the 1.0 code relied on sourcing as fields were set. |
nlapiCreateRecord | record.create({type, isDynamic, defaultValues}) | N/record | 10 transaction, 2 custom record, 5 other | Initialize values go in defaultValues. |
nlapiSubmitRecord | Record.save({enableSourcing, ignoreMandatoryFields}) | N/record | 20 transaction, 4 custom record, 10 other | Options object replaces positional booleans. Returns the internal ID. |
nlapiDeleteRecord | record.delete({type, id}) | N/record | 20 transaction, 4 custom record, 10 other | Same unit cost as save. |
nlapiTransformRecord | record.transform({fromType, fromId, toType, isDynamic}) | N/record | 10 transaction, 2 custom record, 5 other | Save the returned record to commit it. |
nlapiCopyRecord | record.copy({type, id, isDynamic}) | N/record | 10 transaction, 2 custom record, 5 other | Returns an unsaved record. |
nlapiSubmitField | record.submitFields({type, id, values, options}) | N/record | 10 transaction, 2 custom record, 5 other | Values is an object. Only works on inline-editable body fields; others trigger a full load and save. |
nlapiLookupField | search.lookupFields({type, id, columns}) | N/search | 1 | Select fields return an array of {value, text}, not a string. |
nlapiAttachRecord | record.attach({record, to, attributes}) | N/record | 10 | record and to are {type, id} objects. |
nlapiDetachRecord | record.detach({record, from, attributes}) | N/record | 10 | Mirror of attach. |
nlapiVoidTransaction | transaction.void({type, id}) | N/transaction | 10 | Behaviour depends on the Void Transactions Using Reversing Journals preference. |
nlapiSearchRecord | search.create({type, filters, columns}).run().getRange({start, end}) | N/search | getRange 10 per call; create 0 | 1.0 returned up to 1000 rows directly. In 2.x use runPaged or getRange in 1000-row pages. |
nlapiCreateSearch | search.create | N/search | 0 | Filters can be filter objects or a filter expression array. |
nlapiLoadSearch | search.load({id}) | N/search | 5 | id accepts the customsearch script ID or internal ID. |
nlobjSearchFilter | search.createFilter({name, join, operator, values}) | N/search | 0 | Use search.Operator enums, for example search.Operator.ANYOF. |
nlobjSearchColumn | search.createColumn({name, join, summary, sort}) | N/search | 0 | Read results with result.getValue(column) or getValue({name, join}). |
nlobjSearchResultSet.forEachResult | ResultSet.each(callback) | N/search | 10 | Return true to continue; stops at 4000 results. |
nlapiGetContext | runtime.getCurrentScript(), runtime.getCurrentUser(), runtime.getCurrentSession() | N/runtime | 0 | Script parameters: runtime.getCurrentScript().getParameter({name}). |
nlobjContext.getRemainingUsage | runtime.getCurrentScript().getRemainingUsage() | N/runtime | 0 | Same idea, different object. |
nlapiGetUser | runtime.getCurrentUser().id | N/runtime | 0 | Property, not a method. |
nlapiGetRole | runtime.getCurrentUser().role | N/runtime | 0 | Returns the internal ID; roleId gives the script ID. |
nlapiGetSubsidiary | runtime.getCurrentUser().subsidiary | N/runtime | 0 | Department and location follow the same pattern. |
nlapiScheduleScript | task.create({taskType: task.TaskType.SCHEDULED_SCRIPT, scriptId, deploymentId, params}).submit() | N/task | 20 for submit | Map/Reduce uses task.TaskType.MAP_REDUCE. |
nlapiYieldScript, nlapiSetRecoveryPoint | No equivalent | N/A | N/A | Redesign long scheduled jobs as Map/Reduce, which yields and resumes automatically. |
nlapiSendEmail | email.send({author, recipients, subject, body, attachments, relatedRecords}) | N/email | 20 | relatedRecords replaces the records parameter. |
nlapiRequestURL | https.request / https.get / https.post (or http.*) | N/https | 10 | Response body is response.body, status is response.code. |
nlapiResolveURL | url.resolveRecord, url.resolveScript, url.resolveTaskLink, url.resolveDomain | N/url | 0 | Split by URL type instead of one function with a type string. |
nlapiSetRedirectURL | redirect.toRecord, redirect.toSuitelet, redirect.toTaskLink, redirect.redirect | N/redirect | 0 | Suitelet response redirects can also use response.sendRedirect. |
nlapiLogExecution | log.debug, log.audit, log.error, log.emergency | N/log | 0 | Global log object; the deployment Log Level filters what is stored. |
nlapiCreateError | error.create({name, message, notifyOff}) | N/error | 0 | Throw the returned object. |
nlapiLoadFile | file.load({id}) | N/file | 10 | id accepts internal ID or File Cabinet path. |
nlapiCreateFile | file.create({name, fileType, contents, folder}) | N/file | 0 | Set folder before File.save(). |
nlapiSubmitFile | File.save() | N/file | 20 | Returns the file internal ID. |
nlapiCreateForm | serverWidget.createForm({title, hideNavBar}) | N/ui/serverWidget | 0 | Added field IDs must start with custpage. |
nlapiCreateList | serverWidget.createList({title}) | N/ui/serverWidget | 0 | Suitelet only. |
nlapiCreateAssistant | serverWidget.createAssistant({title}) | N/ui/serverWidget | 0 | Suitelet only. |
nlapiDateToString, nlapiFormatCurrency | format.format({value, type: format.Type.DATE | CURRENCY}) | N/format | 0 | Uses the user's date and number preferences. |
nlapiStringToDate | format.parse({value, type: format.Type.DATE}) | N/format | 0 | Returns a Date object. setValue on date fields expects Date, not a string. |
nlapiXMLToPDF | render.xmlToPdf({xmlString}) | N/render | 10 | Returns a file.File object. |
nlapiCreateTemplateRenderer | render.create() | N/render | 0 | Then setTemplateById or templateContent, addRecord, renderAsPdf. |
nlapiGetFieldValue / nlapiSetFieldValue | Record.getValue({fieldId}) / Record.setValue({fieldId, value}) | N/record, N/currentRecord | 0 | Values are typed: checkboxes are booleans, dates are Date objects. |
nlapiGetFieldText / nlapiSetFieldText | Record.getText / Record.setText | N/record | 0 | getText on a standard mode record works only after setText or load. |
nlapiGetLineItemValue / nlapiSetLineItemValue | Record.getSublistValue / setSublistValue({sublistId, fieldId, line, value}) | N/record | 0 | Lines are 0-based in 2.x, 1-based in 1.0. Sublist IDs are lower case (item, not Item). |
nlapiGetLineItemCount | Record.getLineCount({sublistId}) | N/record | 0 | Use it as the loop bound: lines run from 0 to count - 1. |
nlapiSelectNewLineItem / nlapiCommitLineItem | Record.selectNewLine / Record.commitLine({sublistId}) | N/record (dynamic), N/currentRecord | 0 | Dynamic mode only; use setCurrentSublistValue between select and commit. |
nlapiFindLineItemValue | Record.findSublistLineWithValue({sublistId, fieldId, value}) | N/record | 0 | Returns a 0-based index or -1. |
nlapiGetNewRecord / nlapiGetOldRecord | context.newRecord / context.oldRecord | Entry point context | 0 | Read-only in afterSubmit; reload to change and save. |
nlapiInitiateWorkflow / nlapiTriggerWorkflow | workflow.initiate / workflow.trigger | N/workflow | 20 | Workflow ID accepts the customworkflow script ID. |
nlapiExchangeRate | currency.exchangeRate({source, target, date}) | N/currency | 10 | Source and target accept ISO code or internal ID. |
Mappings follow the SuiteScript 1.0 to SuiteScript 2.x API Map in NetSuite help. Governance values are the SuiteScript 2.x unit costs; recheck them in help for your release. 2.1 scripts declare @NApiVersion 2.1 in the JSDoc header.
Get a SuiteScript 1.0 migration estimate
Send us your script list (names, types, line counts) and we will return a reviewed migration order and the scripts that need redesign rather than a rewrite.
Optional. The tool and every export work without this. No spam.
How to use it
Search for the 1.0 function name you are replacing, for example nlapiSubmitField. The 2.x column gives the call and its options object, Module tells you which define dependency to add, and the governance column shows the 2.x unit cost so you can recheck the script's budget. The Gotcha column is where most migration bugs hide: typed values, 0-based line indexes and different return shapes. Work through a script top to bottom, list every distinct nlapi call, and look each one up before you touch the logic. Rows marked No equivalent mean a redesign, not a rename.
SuiteScript 2.1 basics you need for the rewrite
A 2.1 script starts with a JSDoc block containing @NApiVersion 2.1 and @NScriptType (for example UserEventScript or MapReduceScript), then a define call that loads modules such as N/record and N/search and returns the entry points. 2.1 runs on a newer JavaScript engine and accepts ES2019-era syntax such as let, const, arrow functions, destructuring and spread. 1.0 and 2.x APIs cannot be mixed in one file. A 1.0 script record cannot simply point to a 2.1 file; create a new script record, deploy it, test, then set the old deployment to not deployed. Recreate the script parameters on the new script record and value them on the new deployment.
Worked example
A 1.0 user event calls nlapiLookupField('customer', custId, 'creditlimit'), then nlapiSubmitField('salesorder', soId, 'custbody_hold', 'T'). In 2.1 this becomes search.lookupFields({type: search.Type.CUSTOMER, id: custId, columns: ['creditlimit']}).creditlimit, then record.submitFields({type: record.Type.SALES_ORDER, id: soId, values: {custbody_hold: true}}). Note the checkbox is now a boolean true instead of 'T'. If the lookup were a select field, the result would be an array of objects with value and text, so code that compared a string would silently fail. Governance stays the same in this example: 1 unit for the lookup and 10 for submitFields on a transaction.
Common mistakes
The three bugs that show up in almost every migration are 1-based line loops copied into 2.x, so the last line is skipped or line 0 is missed; string dates passed to setValue on a date field, which throws INVALID_FLD_VALUE; and searches that returned 1000 rows in 1.0 now returning nothing because run() was called but getRange or each was not. Also watch nlapiYieldScript loops: there is no yield in 2.x scheduled scripts, so long jobs belong in Map/Reduce. Finally, 2.x context objects differ per entry point, so check context.type against context.UserEventType enums instead of the string 'create'.
- Loop lines from 0 to getLineCount() - 1
- Pass Date objects to date fields, booleans to checkboxes
- Use runPaged for result sets over a few thousand rows
Frequently Asked Questions
Should I migrate SuiteScript 1.0 to 2.0 or 2.1?
Target 2.1 for new work. It uses the same modules as 2.0 but supports modern JavaScript syntax, and Oracle documents new capabilities against 2.x. 1.0 still runs, but Oracle no longer adds new features to it and new APIs are not exposed to it. Migrate the scripts you change most often first.
What replaces nlapiSearchRecord in SuiteScript 2.1?
search.create with type, filters and columns, then run() and either getRange({start: 0, end: 1000}) or each(callback). For more than a few thousand rows use search.runPaged and fetch each page. You can also query with N/query or query.runSuiteQL if SuiteQL is a better fit for the joins you need.
Is there a SuiteScript 2.x equivalent of nlapiYieldScript?
No. Scheduled scripts in 2.x cannot yield and resume. Long-running work should be rewritten as a Map/Reduce script, which splits input into map and reduce stages, manages governance per stage and yields automatically based on the deployment's yield settings.
Can I call SuiteScript 1.0 functions from a 2.1 script?
No. A script file is either 1.0 or 2.x. The nlapi functions are not available in 2.x scripts and 2.x modules are not available in 1.0. During migration you can run both versions side by side as separate script records and deployments, then turn off the old one.
If you want a private AI assistant that reads your legacy SuiteScript and drafts the 2.1 version for your developers to review, running on your own servers, we can set that up.
Related Tools
NetSuite SuiteScript Governance Units by Script Type
A lookup of governance unit limits for each SuiteScript script type and the design guidance that follows from them.
NetSuiteNetSuite Script Deployment Fields and Status Reference
What each field on a Script Deployment record does, which script types have it, and the setting to use for testing and for production.
NetSuiteNetSuite Script ID Prefixes Reference (custbody, custcol, customrecord)
The prefix NetSuite applies to each custom object and field type, where you create it, and how to reference it in SuiteScript, SuiteQL and REST.
Developer ToolsNetSuite SuiteScript 2.1 RESTlet Skeleton Generator
Fill in a script name, record type and fields to get a SuiteScript 2.1 RESTlet with GET lookup, POST create, logging and error handling.
More Developer Tools tools
See all- NetSuite API Retry Backoff Wait Time Calculator
- NetSuite OAuth 2.0 Access Token Refresh Calculator
- NetSuite REST Web Services Headers and Query Parameters Reference
- NetSuite REST API SuiteQL Request Generator
- NetSuite REST API Sales Order and Journal Entry Request Generator
- NetSuite Token-Based Auth OAuth 1.0 Signature Code Generator
- NetSuite OAuth 2.0 Client Credentials (M2M) JWT Code Generator
- SuiteScript 2.1 Scheduled Script Generator for NetSuite
- SuiteScript 2.1 Client Script Generator for NetSuite
Keep exploring
Free tools
- SuiteScript 1.0 to 2.1 Migration Checklist for NetSuiteA five-stage checklist for moving NetSuite scripts from SuiteScript 1.0 to 2.1: inventory, design, code changes, deployment cutover and testing.
- SuiteScript 2.x Modules ReferenceA searchable table of the SuiteScript 2.x N/ modules, what each one is for and the methods you reach for most.
- NetSuite SuiteScript 2.1 RESTlet Skeleton GeneratorFill in a script name, record type and fields to get a SuiteScript 2.1 RESTlet with GET lookup, POST create, logging and error handling.
ERP fixes and how-tos
- N/query versus N/search: choosing the right SuiteScript data access moduleCompare NetSuite's N/query (SuiteQL) and N/search modules for performance, governance cost and capability, and decide which to use for joins, aggregation, sublist reads and large exports in SuiteScript 2.1.
- Fix NetSuite MODULE_DOES_NOT_EXIST in SuiteScript 2.xMODULE_DOES_NOT_EXIST means NetSuite cannot resolve a define() dependency. Check the file path, file cabinet location, extension and SDF deployment order.
- Avoiding governance limit errors in NetSuite Map/Reduce scriptsUnderstand how governance is allocated per key in NetSuite Map/Reduce scripts, why SSS_USAGE_LIMIT_EXCEEDED still happens, and how to design getInputData, map, reduce and summarize stages that do not time out.