Free Resource · NowxLabs

JavaScript for
ServiceNow Developers

From core syntax to GlideRecord, Business Rules, and real platform scripting patterns. A beginner-friendly guide built for your ServiceNow journey.

17Sections
50+Code examples
5Quiz questions
Variables & Data Types
Where every program starts — storing information

What is a variable? Think of it like a labeled box. You put something inside (a number, some text, etc.) and use the label to find it later. In JavaScript, we have three ways to create these boxes: var, let, and const.

let vs const vs var Must Know

const = the box is sealed. You can't replace what's inside. Use this by default — it prevents accidental changes.
let = the box is open. You can swap what's inside. Use this when you need to change the value later.
var = the old way. Don't use it — it has weird scoping rules that cause bugs.

var — Avoid This

var name = "John"; var name = "Jane"; // No error! Silently overwrites if (true) { var x = 5; // Leaks outside the if-block! } console.log(x); // 5 — even though x was inside the if

let & const — Always Use These

const name = "John"; name = "Jane"; // TypeError! Can't reassign const let score = 0; score = 100; // OK! let allows reassignment if (true) { let y = 5; // Stays inside the if-block } console.log(y); // ReferenceError! y doesn't exist here
Data Types — The 7 Kinds of Information

JavaScript stores information in different "types." Why does it matter? Because the type determines what you can DO with the data. You can multiply two numbers, but you can't multiply two strings.

TypeWhat It IsExampleIn One Sentence
NumberAny number (integer or decimal)42, 3.14, -7Used for counting, measuring, calculating
StringText, wrapped in quotes"hello", 'world'Used for names, descriptions, messages
BooleanTrue or falsetrue, falseUsed for yes/no decisions
UndefinedVariable declared but no value givenlet x;"I created the box but haven't put anything in it"
NullIntentionally emptylet x = null;"I deliberately put nothing in this box"
ObjectA collection of key-value pairs{name: "John"}Like a contact card with multiple fields
ArrayAn ordered list of items[1, 2, 3]Like a shopping list — items in order
Think of It This Way

Number = a thermometer reading. String = a name tag. Boolean = a light switch. Undefined = an empty shelf. Null = a shelf labeled "empty on purpose." Object = a file folder with labeled tabs. Array = a numbered list.

How to Check a Type Hack

Use typeof to ask JavaScript "what type is this?" It returns a string telling you the type. Super useful for debugging!

javascript
typeof 42 // "number" typeof "hello" // "string" typeof true // "boolean" typeof undefined // "undefined" typeof null // "object" ← This is a famous JS bug! null is NOT an object. typeof {} // "object" typeof [1,2] // "object" ← Arrays also say "object". Use Array.isArray() instead. typeof function(){} // "function"
Operators
How to compare, combine, and manipulate values
== vs === — The Most Important Lesson Critical

== (double equals) checks if two values are "sort of equal" — it converts types first. === (triple equals) checks if they are exactly equal — same value AND same type. Always use ===. It prevents bugs.

== (Loose) — Unpredictable

1 == "1" // true — JS converts string to number! 0 == false // true — 0 and false are both "falsy" "" == 0 // true — empty string equals zero?? null == undefined // true — they're different types though!

=== (Strict) — Predictable

1 === "1" // false — number is NOT a string ✅ 0 === false // false — number is NOT boolean ✅ "" === 0 // false — string is NOT number ✅ null === undefined // false — null is NOT undefined ✅
ServiceNow Tip

When you compare field values from GlideRecord, they're often strings. current.getValue('priority') returns a string like "1", not the number 1. That's why === matters — you need to know if you're comparing strings or numbers.

Short-Circuiting — A Smart Shortcut Hack

&& and || don't just return true/false. They return the actual value that decided the result. This is incredibly useful for providing defaults or conditional assignments.

javascript
// || returns the FIRST truthy value (or the last one if all falsy) const name = "" || "Anonymous"; // "Anonymous" — "" is falsy, so skip it const role = "Admin" || "User"; // "Admin" — "Admin" is truthy, return it // && returns the FIRST falsy value (or the last one if all truthy) const result = true && "success"; // "success" — both truthy, return last const fail = 0 && "hello"; // 0 — 0 is falsy, return it and stop // ?? (nullish coalescing) — like || but ONLY null/undefined trigger fallback const count = 0 ?? 10; // 0 — 0 is valid, not null/undefined const val = null ?? 10; // 10 — null triggers the fallback // ServiceNow example: provide a default category const cat = current.getValue('category') || 'Inquiry';
Ternary Operator — One-Line If/Else

Instead of writing a 5-line if/else just to assign a value, use the ternary: condition ? valueIfTrue : valueIfFalse. It's like asking a question: "Is this true? If yes, give me A. If no, give me B."

javascript
// Long way: let label; if (priority === "1") { label = "Critical"; } else { label = "Normal"; } // Short way (ternary): const label = priority === "1" ? "Critical" : "Normal"; // ServiceNow example: const msg = current.getValue('state') === '6' ? 'Incident resolved' : 'Still open';
Strings
Working with text — the most common data type in ServiceNow

A string is just text — anything wrapped in quotes. You'll work with strings constantly in ServiceNow because almost every field value you read is a string. Here's what you need to know.

Template Literals — No More Messy Concatenation Must Know

Template literals use backticks (``) instead of regular quotes. The magic? You can embed variables directly inside with ${variableName}. No more + + + everywhere.

Old Way — Hard to Read

var msg = "Incident " + incNum + " was created by " + caller + " on " + date;

New Way — Clean and Clear

const msg = `Incident ${incNum} was created by ${caller} on ${date}`;
javascript
// You can also put expressions inside ${} const total = `Total: $${(price * quantity).toFixed(2)}`; // Multi-line strings — just press Enter! const email = `Dear ${caller}, Your incident ${incNum} has been resolved. Thank you, IT Support`;
String Methods You'll Use Every Day

Methods are actions you can perform on a string. Think of them as tools: .trim() is like scissors (cuts whitespace), .includes() is like a search function.

MethodWhat It DoesExampleResult
.lengthHow many characters"hello".length5
.toUpperCase()ALL CAPS"hello".toUpperCase()"HELLO"
.toLowerCase()all lowercase"HELLO".toLowerCase()"hello"
.trim()Remove extra spaces from edges" hi ".trim()"hi"
.includes()Does it contain this text?"hello".includes("ell")true
.startsWith()Does it begin with this?"INC001".startsWith("INC")true
.indexOf()Where does this text appear?"hello".indexOf("l")2
.slice(start, end)Cut out a piece"hello".slice(0,3)"hel"
.replace()Replace first match"ha".replace("h","b")"ba"
.split()String → Array"a,b,c".split(",")["a","b","c"]
Quick Hacks

Reverse a string: "hello".split('').reverse().join('') → "olleh"

Capitalize first letter: "hello"[0].toUpperCase() + "hello".slice(1) → "Hello"

Quick number to string: 42 + "" → "42"

Quick string to number: +"42" → 42

Arrays
Ordered lists — used everywhere in ServiceNow

An array is a list of items, in order, wrapped in square brackets. Think of it like a numbered list: item 0, item 1, item 2... (yes, we start counting at 0, not 1). In ServiceNow, you'll work with arrays when handling multiple records, lists of approvers, CI relationships, etc.

Creating and Accessing Arrays
javascript
// Create an array const fruits = ["apple", "banana", "cherry"]; // Access items by position (starts at 0!) fruits[0] // "apple" ← first item fruits[1] // "banana" ← second item fruits[2] // "cherry" ← third item // Last item? Use .at(-1) fruits.at(-1) // "cherry" // How many items? fruits.length // 3
Adding and Removing Items
javascript
const list = ["A", "B"]; // Add to END list.push("C"); // ["A","B","C"] // Add to START list.unshift("Z"); // ["Z","A","B","C"] // Remove from END list.pop(); // removes "C", returns it // Remove from START list.shift(); // removes "Z", returns it // Remove at specific position: splice(index, howMany) list.splice(1, 1); // removes 1 item at position 1
The Big Three: map, filter, reduce Must Know

These three methods are the most important array methods. They replace most loops you'd write. Think of them this way:

filter = "Keep only the items that match" (like a coffee filter keeps liquid, removes grounds)

map = "Transform each item" (like a map translates one language to another)

reduce = "Combine all items into one" (like reducing sauce — many ingredients become one)

javascript
const incidents = [ { number: 'INC001', priority: 1, state: 'New' }, { number: 'INC002', priority: 3, state: 'In Progress' }, { number: 'INC003', priority: 1, state: 'New' }, { number: 'INC004', priority: 2, state: 'Resolved' }, ]; // FILTER: Keep only priority 1 incidents const critical = incidents.filter(inc => inc.priority === 1); // Result: [{number:'INC001',...}, {number:'INC003',...}] // MAP: Extract just the incident numbers const numbers = incidents.map(inc => inc.number); // Result: ['INC001','INC002','INC003','INC004'] // REDUCE: Count by priority const counts = incidents.reduce((acc, inc) => { acc[inc.priority] = (acc[inc.priority] || 0) + 1; return acc; }, {}); // Result: {1: 2, 2: 1, 3: 1} // CHAIN THEM: Get numbers of critical new incidents const result = incidents .filter(inc => inc.priority === 1 && inc.state === 'New') .map(inc => inc.number); // Result: ['INC001', 'INC003']
Sort — The Hidden Trap Gotcha

By default, .sort() converts everything to strings and sorts alphabetically. That means [10, 2, 1] becomes [1, 10, 2] — because "10" comes before "2" alphabetically! Always provide a compare function for numbers.

javascript
[10, 9, 8, 100].sort(); // [10, 100, 8, 9] — WRONG! [10, 9, 8, 100].sort((a,b) => a-b); // [8, 9, 10, 100] — Correct ✅ // Sort objects by a property: incidents.sort((a, b) => a.priority - b.priority);
Quick Hacks

Remove duplicates: [...new Set([1,2,2,3])] → [1,2,3]

Check if array has item: [1,2,3].includes(2) → true

Combine arrays: [...arr1, ...arr2]

Remove falsy values: [0, "hi", null, "", 5].filter(Boolean) → ["hi", 5]

Objects
Key-value pairs — how ServiceNow represents records

An object is like a contact card: it has labeled fields (keys) and their values. {name: "John", role: "Admin"}. In ServiceNow, every GlideRecord is essentially an object with field names as keys and field values as values. Understanding objects is essential for ServiceNow development.

Creating and Using Objects
javascript
// Create an object — like filling out a form const user = { name: "John", role: "Admin", active: true, greet() { // You can put functions inside objects! return `Hi, I'm ${this.name}`; // 'this' refers to the object itself } }; // Access a value with dot notation user.name // "John" user.role // "Admin" // Access a value with bracket notation (useful for dynamic keys) user["name"] // "John" const key = "role"; user[key] // "Admin" // Change a value user.name = "Jane"; // Add a new field user.email = "jane@company.com"; // Call the method inside user.greet(); // "Hi, I'm Jane"
Destructuring — Unpack Values Quickly Hack

Instead of writing const name = user.name; const role = user.role; over and over, you can "unpack" multiple values in one line.

javascript
// Old way — repetitive const name = user.name; const role = user.role; // Destructuring — clean and fast const { name, role } = user; // Creates two variables at once! // Rename while destructuring const { name: userName } = user; // userName = "Jane" // With default value const { department = "IT" } = user; // "IT" if department doesn't exist // Spread operator — copy or merge objects const copy = { ...user }; // Shallow copy const updated = { ...user, role: "Manager" }; // Copy + override
Useful Object Methods
MethodWhat It DoesExample
Object.keys(obj)Get all key names as an array["name","role"]
Object.values(obj)Get all values as an array["Jane","Admin"]
Object.entries(obj)Get [key, value] pairs[["name","Jane"],...]
JSON.stringify(obj)Convert object to JSON stringFor logging or sending data
JSON.parse(str)Convert JSON string to objectFor receiving data
Functions
Reusable blocks of code — the building blocks of every script

A function is a recipe. You write the steps once, then use (call) it whenever you need it. Instead of copy-pasting the same code everywhere, you put it in a function and call it by name.

Three Ways to Write Functions
javascript
// 1. Function Declaration — can be called before it's defined (hoisted) function greet(name) { return `Hello, ${name}!`; } greet("John"); // "Hello, John!" // 2. Function Expression — stored in a variable, NOT hoisted const greet = function(name) { return `Hello, ${name}!`; }; // 3. Arrow Function — shortest syntax, no own 'this' const greet = (name) => `Hello, ${name}!`; // Arrow with multiple lines needs curly braces + return const add = (a, b) => { const sum = a + b; return sum; // Must use 'return' with curly braces }; // Arrow returning an object — wrap in parentheses! const makeUser = (name) => ({ name, active: true });
Arrow vs Regular: The 'this' Trap Gotcha

The biggest difference: arrow functions do NOT have their own this. They borrow this from the surrounding code. Regular functions create their own this. Rule of thumb: Use arrow functions for callbacks. Use regular functions for object methods.

Arrow in Object Method — Broken

const obj = { name: "John", greet: () => { return this.name; // 'this' is NOT obj! } }; obj.greet(); // undefined ❌

Regular Function in Object — Works

const obj = { name: "John", greet() { return this.name; // 'this' = obj ✅ } }; obj.greet(); // "John" ✅
Closures — Functions Remember Their Birthplace Must Know

A closure is a function that "remembers" the variables from the place where it was created, even after that place has finished running. Think of it like a person who remembers their hometown — even after they move away, they still carry those memories.

javascript
function createCounter() { let count = 0; // This variable is "private" return { // Return an object with functions increment() { return ++count; }, // Can still see 'count' decrement() { return --count; }, getCount() { return count; } }; } const counter = createCounter(); counter.increment(); // 1 counter.increment(); // 2 counter.getCount(); // 2 // Nobody can directly access or change 'count' — it's protected! // ServiceNow uses this pattern in Script Includes
If/Else & Switch
Making decisions in your code
Falsy Values — Memorize These 7 Must Know

When JavaScript checks an if condition, it converts the value to true/false. Only these 7 values count as false — everything else is true. This is the #1 source of subtle bugs.

javascript
// The ONLY 7 falsy values: false // obvious 0 // zero is falsy -0 // negative zero is falsy "" // empty string is falsy null // null is falsy undefined // undefined is falsy NaN // Not-a-Number is falsy // EVERYTHING ELSE is truthy! Even: "0" // truthy — it's a non-empty string! "false" // truthy — it's a non-empty string! [] // truthy — even an EMPTY array! {} // truthy — even an EMPTY object!
Switch Statement — Cleaner Than Many Ifs

When you need to compare one value against many possibilities, switch is cleaner than a long chain of if/else if. Don't forget break — without it, the code "falls through" and runs the next case too!

javascript — ServiceNow state check
const state = current.getValue('state'); switch (state) { case '1': // New gs.addInfoMessage('New incident created'); break; // ← Don't forget this! case '2': // In Progress gs.addInfoMessage('Work in progress'); break; case '6': // Resolved notifyCaller(); break; default: // If nothing matches gs.log('Unknown state: ' + state); }
Loops
Repeating actions without repeating code

Loops let you do the same thing to every item in a collection. In ServiceNow, you'll use loops constantly — to process every record from a GlideRecord query, every item in an array, etc.

Which Loop to Use When
LoopWhen to Use ItExample
for...ofLoop through array values (most common)for (const item of arr)
for...inLoop through object keysfor (const key in obj)
for (let i=0; ...)When you need the index numberfor (let i=0; i<arr.length; i++)
.forEach()Simple array iteration (can't break out early)arr.forEach(item => ...)
whileWhen you don't know how many times to loopGlideRecord's while(gr.next())
Loop Examples
javascript
// for...of — the one you'll use most for arrays const incs = ['INC001', 'INC002', 'INC003']; for (const inc of incs) { gs.print(inc); // INC001, then INC002, then INC003 } // for...in — for objects const config = { host: 'localhost', port: 443 }; for (const key in config) { gs.print(key + ' = ' + config[key]); // host = localhost, port = 443 } // while — THE ServiceNow GlideRecord pattern var gr = new GlideRecord('incident'); gr.addQuery('active', true); gr.query(); while (gr.next()) { // For each record found... gs.print(gr.getValue('number')); } // break — exit the loop immediately // continue — skip this iteration, move to next
ES6+ Shortcuts
Modern JavaScript features that save you time

ES6 (released in 2015) added many shortcuts that make JavaScript easier to write and read. Most of these work in ServiceNow (especially on newer versions). Here are the ones you should know.

javascript — ES6+ cheat sheet
// 1. const/let — already covered, but use const by default // 2. Template literals — already covered. Use backticks + ${} // 3. Arrow functions const double = n => n * 2; // One-liner const add = (a, b) => a + b; // Multiple params // 4. Destructuring const { name, role } = user; // Object destructuring const [first, second] = [10, 20]; // Array destructuring // 5. Spread operator const newArr = [...oldArr, newItem]; // Copy + add const newObj = { ...oldObj, key: 'val' }; // Copy + override // 6. Default parameters function search(table = 'incident') { ... } // Default if not provided // 7. Optional chaining — safe property access (Tokyo+) const mgr = current?.assignment_group?.manager?.name; // No error if any part is null // 8. Nullish coalescing — better default values (Tokyo+) const desc = shortDesc ?? 'No description'; // Only triggers for null/undefined, not "" // 9. Classes class IncidentHelper { constructor(gr) { this.gr = gr; } isCritical() { return this.gr.priority === 1; } }
ServiceNow Compatibility

Older SN instances run on the Rhino engine, which doesn't support ?? and ?.. If you're on Tokyo or later, you're fine. For older instances, stick to || for defaults and manual if checks for safe property access.

Error Handling
How to handle problems gracefully

When something goes wrong in your code, JavaScript normally stops and throws an error. try/catch lets you catch that error and handle it gracefully instead of crashing. Think of it like a safety net under a trapeze artist.

javascript
try { // Code that might fail const result = riskyOperation(); } catch (error) { // If anything fails, this runs instead of crashing // error.message — the error description // error.name — the error type gs.logError('Operation failed: ' + error.message, 'MyScript'); } finally { // This ALWAYS runs — whether success or failure // Use for cleanup: closing connections, etc. cleanup(); } // You can also THROW your own errors function validatePriority(priority) { if (priority < 1 || priority > 5) { throw new Error(`Invalid priority: ${priority}. Must be 1-5.`); } return true; }
Debugging in ServiceNow

gs.log(message, source) → General log

gs.info(message) → Info level

gs.warn(message) → Warning level

gs.error(message) → Error level

Find your logs: System Logs → System Log → All

Regex Basics
Pattern matching for validation

Regular expressions (regex) are like search patterns on steroids. You use them to check if text matches a pattern (like "is this a valid email?") or to find/replace parts of text.

Common ServiceNow Patterns
PatternWhat It MatchesUse Case
/^INC\d{7}$/INC + exactly 7 digitsValidate incident number
/^\S+@\S+\.\S+$/Basic email formatEmail validation
/^\d+$/Only digitsPhone/ID validation
/^[a-f0-9]{32}$/32 hex characterssys_id format
javascript
// test() — Does the string match? Returns true/false const isEmail = /^\S+@\S+\.\S+$/.test('user@example.com'); // true const isINC = /^INC\d{7}$/.test('INC0012345'); // true // match() — Find all matches 'INC001 and INC002'.match(/INC\d{3}/g); // ['INC001','INC002'] // replace() — Swap matched text 'Hello World'.replace(/\s+/g, ' '); // 'Hello World' (remove extra spaces)
How ServiceNow Uses JavaScript
What you need to know about the platform

ServiceNow uses JavaScript on two sides: the server (where data lives and business rules run) and the client/browser (where users interact with forms). Different scripts run in different places, and you have access to different APIs depending on where you are.

Server vs Client — The Big Split SN Core
Runs OnScript TypesCan Use DOM?Can Use GlideRecord?In Plain English
ServerBusiness Rules, Script Includes, WorkflowsNoYesWorks directly with the database
Client/BrowserClient Scripts, UI Policies, UI PagesYesNo (use GlideAjax)Works with the form the user sees
Think of It Like a Restaurant

Server-side = the kitchen. It has access to the ingredients (database), processes orders (business rules), and sends food out. It never sees the customer directly.

Client-side = the waiter and menu. It interacts with the customer (user), shows options (form), and takes requests. It can't cook (no GlideRecord) — it has to ask the kitchen (GlideAjax).

GlideRecord — The Most Important API
How to read, create, update, and delete records

GlideRecord is how you work with the database in ServiceNow. It's like a cursor that moves through records one at a time. You'll use this every single day as a ServiceNow developer. Let's learn it step by step.

Step 1: Query (Find Records)

Querying means "find me records that match these conditions." Think of it like searching a filing cabinet.

javascript — Query records
// Step 1: Create a GlideRecord object for the 'incident' table var gr = new GlideRecord('incident'); // Step 2: Add filters (like a WHERE clause in SQL) gr.addQuery('active', true); // Active incidents only gr.addQuery('priority', 'IN', '1,2'); // Priority 1 or 2 gr.addNullQuery('assigned_to'); // Not yet assigned // Step 3: Sort the results gr.orderBy('priority'); // Ascending gr.orderByDesc('sys_created_on'); // Newest first // Step 4: Always set a limit! (Safety first) gr.setLimit(100); // Step 5: Execute the query! gr.query(); // Step 6: Loop through results while (gr.next()) { gs.print(gr.getValue('number')); // Raw value from DB gs.print(gr.getDisplayValue('priority')); // Human-readable: "High" gs.print(gr.number); // Dot notation (read only) }
Step 2: Get a Single Record
javascript
var gr = new GlideRecord('incident'); if (gr.get('sys_id_here')) { // Returns true if record found gs.print(gr.getValue('number')); } else { gs.log('Record not found!'); }
Step 3: Insert (Create a New Record)
javascript
var gr = new GlideRecord('incident'); gr.initialize(); // ALWAYS call this first for new records! gr.setValue('short_description', 'New issue'); gr.setValue('caller_id', gs.getUserID()); // Current user gr.setValue('priority', '3'); var sysId = gr.insert(); // Returns the new sys_id gs.log('Created: ' + sysId);
Step 4: Update a Record
javascript
var gr = new GlideRecord('incident'); if (gr.get('sys_id_here')) { gr.setValue('state', '6'); // Resolved gr.setValue('close_notes', 'Fixed the issue'); gr.update(); // Save the changes! }
Step 5: Delete a Record
javascript
var gr = new GlideRecord('incident'); if (gr.get('sys_id_here')) { gr.deleteRecord(); }
Handy GlideRecord Methods
MethodWhat It DoesWhen to Use
gr.getValue(field)Get the raw database valueAlways, when reading a field
gr.getDisplayValue(field)Get the human-readable valueFor choice fields: "High" instead of "1"
gr.setValue(field, value)Set a field valueWhen modifying a record
gr.changes(field)Did this field change?In Before Business Rules
gr.changesTo(field, val)Did it change TO this value?Trigger action on specific change
gr.isNewRecord()Is this a brand new record?Set defaults only on creation
gr.setAbortAction(true)Block the save/insert/deleteValidation failures
gr.getUniqueValue()Get the sys_idReferencing this record
gr.setLimit(n)Limit how many recordsALWAYS use this!
Other Useful Glide APIs
javascript
// GlideSystem (gs) — System utilities gs.getUserID(); // Current user's sys_id gs.getUserName(); // Current user's username gs.hasRole('admin'); // Does current user have this role? gs.addInfoMessage('msg'); // Green banner on form gs.addErrorMessage('msg'); // Red banner on form gs.nowDateTime(); // Current date/time gs.daysAgo(7); // Date 7 days ago // GlideAggregate — Count, Sum, Average var ga = new GlideAggregate('incident'); ga.addAggregate('COUNT'); ga.groupBy('priority'); ga.query(); while (ga.next()) { gs.print(ga.priority + ': ' + ga.getAggregate('COUNT')); }
ServiceNow Script Types
Where to write what — and why it matters
Business Rules — Server-Side Automation SN Core

Business Rules run automatically when a record is displayed, created, updated, or deleted. There are three types, and knowing which to use is critical.

TypeWhen It RunsBest ForCan Change Values?Needs .update()?
BeforeBefore saving to DBSetting defaults, validationYesNo — auto-saved
AfterAfter saving to DBNotifications, creating related recordsYes (but risky)Yes
DisplayBefore showing the formForm messages, setting g_form valuesOnly display valuesN/A
javascript — Before Business Rule (validation)
(function() { // Set default priority on new records if (current.isNewRecord()) { current.setValue('priority', '3'); } // Validate description length if (current.getValue('short_description').length > 200) { gs.addErrorMessage('Description is too long!'); current.setAbortAction(true); // Block the save! } })();
javascript — After Business Rule (create follow-up)
(function() { // When state changes to Resolved, create a follow-up task if (current.changes('state') && current.getValue('state') === '6') { var task = new GlideRecord('sc_task'); task.initialize(); task.setValue('parent', current.getUniqueValue()); task.setValue('short_description', 'Follow-up: ' + current.getValue('number')); task.insert(); } })();
Client Scripts — Browser-Side Form Behavior
javascript — Client Script (onChange)
function onChange(control, oldValue, newValue, isLoading, isTemplate) { // ALWAYS check isLoading first! // Without this, your script runs on form load for every field if (isLoading) return; // When priority changes to 1, show and require the reason field if (newValue === '1') { g_form.setVisible('reason', true); g_form.setMandatory('reason', true); } else { g_form.setVisible('reason', false); g_form.setMandatory('reason', false); } }
Script Include — Reusable Server-Side Library SN Core

A Script Include is like a library of functions that other scripts can call. Think of it as a toolbox — you put your commonly-used functions in one place and reuse them everywhere. It's also how GlideAjax works (client calls server).

javascript — Script Include
var IncidentUtils = Class.create(); IncidentUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { // Count critical active incidents getCriticalCount: function() { var ga = new GlideAggregate('incident'); ga.addQuery('active', true); ga.addQuery('priority', '1'); ga.addAggregate('COUNT'); ga.query(); if (ga.next()) return ga.getAggregate('COUNT'); return 0; }, type: 'IncidentUtils' });
Common Mistakes That Cause Bugs
Learn from others' pain — avoid these pitfalls

GlideRecord in a Loop (N+1 Problem)

Running a GlideRecord query inside a while loop fires a SEPARATE database call each time. This destroys performance.

// DON'T DO THIS! while (inc.next()) { var user = new GlideRecord('sys_user'); user.get(inc.getValue('assigned_to')); // A new DB query EVERY loop! }

Use Dot-Walking Instead

Dot-walking follows references in a single query. Let ServiceNow handle the join — it's much faster.

// DO THIS instead! while (inc.next()) { var mgrName = inc.assigned_to.manager.name; // One query, join handled by SN! }

current.update() in After Business Rule

Calling current.update() in an After BR triggers Business Rules again — potentially causing an infinite loop!

// After Business Rule — DANGEROUS current.setValue('work_notes', 'Updated'); current.update(); // Triggers BRs again! 💥

Set Values in Before BR (No .update Needed)

In a Before BR, just set the value. It saves automatically when the record saves.

// Before Business Rule — CORRECT current.setValue('work_notes', 'Updated'); // No .update() needed! Saved automatically ✅

Missing setLimit on Queries

Without setLimit(), GlideRecord returns ALL matching records. On large tables, this can bring down the instance.

var gr = new GlideRecord('syslog'); gr.query(); // MILLIONS of rows! 💥

Always Set a Limit

Add setLimit() to every query unless you have a specific reason not to.

var gr = new GlideRecord('syslog'); gr.setLimit(100); // Safe! ✅ gr.query();
The Top 5 Rookie Mistakes
  1. Forgetting isLoading check in Client Scripts → script runs on every field on form load
  2. Using current.update() in After BR → recursive triggers / infinite loops
  3. Missing setLimit on GlideRecord → performance disaster
  4. Using == instead of === → type coercion bugs
  5. GlideRecord inside a loop → N+1 query problem, slow performance
Quick Cheat Sheet
Everything on one page — bookmark this
Type Conversions
String → Number+"42" or parseInt("42", 10)
Number → String42 + "" or String(42)
Any → Boolean!!value
Array → Stringarr.join(",")
String → Arraystr.split(",")
Object → JSONJSON.stringify(obj)
JSON → ObjectJSON.parse(str)
Short-Circuit Patterns
val || defaultUse default if val is falsy
val ?? defaultUse default only if null/undefined
cond && action()Do action only if true
obj?.prop?.method()Safe chain — no error if null
Array One-Liners
Unique values[...new Set(arr)]
Sumarr.reduce((a,b) => a+b, 0)
MaxMath.max(...arr)
Last itemarr.at(-1)
Remove falsyarr.filter(Boolean)
Flattenarr.flat()
GlideRecord Quick Ref
Querygr.addQuery(); gr.query();
Get onegr.get('sys_id')
Insertgr.initialize(); gr.setValue(); gr.insert();
Updategr.setValue(); gr.update();
Deletegr.deleteRecord();
Block savecurrent.setAbortAction(true);
Field changed?current.changes('field')
The 7 Falsy Values — Memorize These
false 0 -0 "" null undefined NaN // Everything else is truthy, including: "0", "false", [], {}, function(){}
Quick Quiz
Test what you've learned
1. What does typeof null return in JavaScript?
2. What does [1,2,3].reduce((a,b) => a+b, 0) return?
3. Which Business Rule type should you use to validate data and potentially block a save?
4. What's missing from this GlideRecord insert?
var gr = new GlideRecord('incident'); // ??? gr.setValue('short_description', 'Test'); gr.insert();
5. Why should you always check isLoading in a Client Script onChange function?