Project Structure
This guide explains every file and folder in an SDF project and their purposes.
Complete Project Structure
📁 my-netsuite-project/
│
├── 📄 suitecloud.config.js # Project configuration
├── 📄 manifest.xml # Deployment manifest
├── 📄 deploy.xml # Deployment settings (optional)
├── 📄 package.json # Node.js dependencies
│
├── 📁 src/ # Source files
│ │
│ ├── 📁 FileCabinet/ # Files deployed to File Cabinet
│ │ │
│ │ └── 📁 SuiteScripts/ # Script files
│ │ ├── 📁 Suitelets/ # Suitelet scripts
│ │ ├── 📁 UserEvents/ # User Event scripts
│ │ ├── 📁 ClientScripts/ # Client scripts
│ │ ├── 📁 ScheduledScripts/ # Scheduled scripts
│ │ ├── 📁 MapReduce/ # Map/Reduce scripts
│ │ ├── 📁 RESTlets/ # RESTlet scripts
│ │ ├── 📁 WorkflowActions/ # Workflow Action scripts
│ │ └── 📁 Libraries/ # Shared modules
│ │
│ ├── 📁 Objects/ # Custom object definitions
│ │ ├── customrecord_*.xml # Custom records
│ │ ├── customlist_*.xml # Custom lists
│ │ ├── custworkflow_*.xml # Workflows
│ │ ├── customsearch_*.xml # Saved searches
│ │ ├── customscript_*.xml # Script deployments
│ │ └── ... # Other objects
│ │
│ └── 📁 Templates/ # HTML templates
│ └── *.html # Template files
│
├── 📁 node_modules/ # Dependencies (auto-generated)
│
└── 📁 .git/ # Git version control
How Files Connect
┌─────────────────────────────────────────────────────────────────────────────┐
│ HOW SDF FILES CONNECT │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ manifest.xml │
│ (What features are required) │
└────────────────────────────────┬────────────────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ Script File (.js) │ │ Object File (.xml) │
│ my_suitelet.js │ │ customscript_my_sl.xml │
│ (Contains the code) │ │ (Deployment settings) │
└─────────────────────────────┘ └─────────────────────────────┘
│ │
│ Links to │
└────────────┬────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ NetSuite Account │
│ Script is deployed and accessible at configured URL/trigger │
└─────────────────────────────────────────────────────────────────────────────┘
Core Configuration Files
suitecloud.config.js
Project-level SDF configuration:
module.exports = {
// Where source files are located
defaultProjectFolder: 'src',
// Custom command configurations (optional)
commands: {
// Command-specific settings
}
};
manifest.xml
Defines project metadata and dependencies:
<?xml version="1.0" encoding="UTF-8"?>
<manifest projecttype="ACCOUNTCUSTOMIZATION">
<!-- Project name -->
<projectname>my-netsuite-project</projectname>
<!-- SDF framework version -->
<frameworkversion>1.0</frameworkversion>
<!-- Required NetSuite features -->
<dependencies>
<features>
<feature required="true">CUSTOMRECORDS</feature>
<feature required="true">SERVERSIDESCRIPTING</feature>
<feature required="true">CLIENTSIDESCRIPTING</feature>
</features>
</dependencies>
</manifest>
Common Features
| Feature | Purpose |
|---|---|
| CUSTOMRECORDS | Custom record types |
| SERVERSIDESCRIPTING | Server-side scripts |
| CLIENTSIDESCRIPTING | Client scripts |
| WORKFLOW | Workflow engine |
| ADVANCEDPRINTING | PDF generation |
deploy.xml (Optional)
Controls which objects to deploy:
<?xml version="1.0" encoding="UTF-8"?>
<deploy>
<configuration>
<path>~/Objects/*</path>
</configuration>
<files>
<path>~/FileCabinet/SuiteScripts/*</path>
</files>
<objects>
<path>~/Objects/customscript_*.xml</path>
<path>~/Objects/customrecord_*.xml</path>
</objects>
</deploy>
Script Files
Location
All scripts go under src/FileCabinet/SuiteScripts/:
📁 SuiteScripts/
├── 📁 Suitelets/
│ └── invoice_report_sl.js
├── 📁 UserEvents/
│ └── sales_order_ue.js
├── 📁 ClientScripts/
│ └── customer_form_cs.js
└── 📁 Libraries/
└── common_utils.js
Script File Structure
/**
* @NApiVersion 2.1
* @NScriptType Suitelet
* @NModuleScope SameAccount
*/
define(['N/ui/serverWidget', 'N/search'],
(serverWidget, search) => {
/**
* Main entry point
* @param {Object} context
*/
const onRequest = (context) => {
// Script logic here
};
return { onRequest };
});
Script Type Headers
| Script Type | @NScriptType Value |
|---|---|
| Suitelet | Suitelet |
| User Event | UserEventScript |
| Client Script | ClientScript |
| Scheduled | ScheduledScript |
| Map/Reduce | MapReduceScript |
| RESTlet | Restlet |
| Workflow Action | WorkflowActionScript |
Object Files (XML)
Location
All objects go under src/Objects/:
📁 Objects/
├── customrecord_invoice_approval.xml # Custom record
├── customlist_approval_status.xml # Custom list
├── customscript_invoice_sl.xml # Script deployment
├── customsearch_pending_invoices.xml # Saved search
└── custworkflow_invoice_approval.xml # Workflow
Script Deployment XML
Links a script file to NetSuite:
<?xml version="1.0" encoding="UTF-8"?>
<customscript scriptid="customscript_invoice_sl">
<name>Invoice Report Suitelet</name>
<scripttype>SUITELET</scripttype>
<scriptfile>[/SuiteScripts/Suitelets/invoice_report_sl.js]</scriptfile>
<description>Invoice reporting suitelet</description>
<isinactive>F</isinactive>
<notifyowner>T</notifyowner>
<!-- Deployment configuration -->
<scriptdeployments>
<scriptdeployment scriptid="customdeploy_invoice_sl">
<status>RELEASED</status>
<title>Invoice Report</title>
<isdeployed>T</isdeployed>
<loglevel>DEBUG</loglevel>
<allroles>T</allroles>
</scriptdeployment>
</scriptdeployments>
</customscript>
Script Deployment Flow
┌─────────────────────────────────────────────────────────────────────────────┐
│ SCRIPT DEPLOYMENT RELATIONSHIP │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────┐
│ invoice_report_sl.js │
│ (Script Code) │
│ │
│ • Business logic │
│ • Form creation │
│ • Data processing │
└─────────────┬───────────────┘
│
│ Referenced by
▼
┌─────────────────────────────┐
│ customscript_invoice_sl │
│ (Script Record) │
│ │
│ • Script type: SUITELET │
│ • Script file path │
│ • Owner, notifications │
└─────────────┬───────────────┘
│
│ Contains
▼
┌─────────────────────────────┐
│ customdeploy_invoice_sl │
│ (Script Deployment) │
│ │
│ • URL/trigger settings │
│ • Role permissions │
│ • Status (Testing/Release) │
│ • Log level │
└─────────────────────────────┘
Custom Record XML
<?xml version="1.0" encoding="UTF-8"?>
<customrecordtype scriptid="customrecord_invoice_approval">
<recordname>Invoice Approval</recordname>
<includename>T</includename>
<showid>T</showid>
<customrecordcustomfields>
<customrecordcustomfield scriptid="custrecord_ia_invoice">
<label>Invoice</label>
<fieldtype>SELECT</fieldtype>
<selectrecordtype>-30</selectrecordtype>
</customrecordcustomfield>
<customrecordcustomfield scriptid="custrecord_ia_status">
<label>Status</label>
<fieldtype>SELECT</fieldtype>
<selectrecordtype>[customlist_approval_status]</selectrecordtype>
</customrecordcustomfield>
<customrecordcustomfield scriptid="custrecord_ia_notes">
<label>Approval Notes</label>
<fieldtype>TEXTAREA</fieldtype>
</customrecordcustomfield>
</customrecordcustomfields>
</customrecordtype>
Custom List XML
<?xml version="1.0" encoding="UTF-8"?>
<customlist scriptid="customlist_approval_status">
<name>Approval Status</name>
<customvalues>
<customvalue scriptid="val_pending">
<value>Pending</value>
</customvalue>
<customvalue scriptid="val_approved">
<value>Approved</value>
</customvalue>
<customvalue scriptid="val_rejected">
<value>Rejected</value>
</customvalue>
</customvalues>
</customlist>
Saved Search XML
<?xml version="1.0" encoding="UTF-8"?>
<customsearch scriptid="customsearch_pending_invoices">
<definition>
<searchtype>Transaction</searchtype>
<searchcolumns>
<column>
<name>tranid</name>
</column>
<column>
<name>entity</name>
</column>
<column>
<name>amount</name>
</column>
</searchcolumns>
<searchfilters>
<filter>
<name>type</name>
<operator>is</operator>
<values>VendBill</values>
</filter>
<filter>
<name>status</name>
<operator>is</operator>
<values>open</values>
</filter>
</searchfilters>
</definition>
</customsearch>
Templates Folder
HTML Templates
For Suitelets with custom HTML:
📁 Templates/
├── invoice_report.html
└── email_template.html
Loading Templates in Script
const file = require('N/file');
const template = file.load({
id: './Templates/invoice_report.html'
}).getContents();
File Naming Conventions
| Type | Convention | Example |
|---|---|---|
| Suitelet | {name}_sl.js | invoice_report_sl.js |
| User Event | {name}_ue.js | sales_order_ue.js |
| Client Script | {name}_cs.js | customer_cs.js |
| Scheduled | {name}_ss.js | daily_report_ss.js |
| Map/Reduce | {name}_mr.js | data_import_mr.js |
| RESTlet | {name}_rl.js | customer_api_rl.js |
| Library | {name}_lib.js | common_utils_lib.js |
| Custom Record | customrecord_{name}.xml | customrecord_approval.xml |
| Custom List | customlist_{name}.xml | customlist_status.xml |
| Script Deploy | customscript_{name}.xml | customscript_report_sl.xml |
Best Practices
Folder Organization
📁 SuiteScripts/
├── 📁 Suitelets/
│ ├── 📁 Reports/ # Group by function
│ │ ├── sales_report_sl.js
│ │ └── inventory_report_sl.js
│ └── 📁 Forms/
│ └── data_entry_sl.js
├── 📁 UserEvents/
│ ├── 📁 SalesOrder/ # Group by record type
│ │ └── so_validation_ue.js
│ └── 📁 Invoice/
│ └── inv_automation_ue.js
└── 📁 Libraries/
├── error_handler_lib.js # Shared utilities
├── format_lib.js
└── search_lib.js
Object Naming
- Use lowercase with underscores
- Include type indicator:
customrecord_,customlist_, etc. - Be descriptive:
customrecord_invoice_approvalnotcustomrecord_ia
Import Existing Objects
To import existing objects from NetSuite:
Ctrl+Shift+P → SuiteCloud: Import Objects
Or via terminal:
suitecloud object:import --type customrecord
suitecloud object:import --type savedsearch
suitecloud object:import --type workflow
Next Steps
- Suitelet Development - Create your first script
- Custom Records - Define data structures
- Deploy to Sandbox - Test your project