How to Update Existing Records in ServiceNow






Mastering Record Updates in ServiceNow: A Developer’s Guide to Glide APIs



Mastering Record Updates in ServiceNow: A Developer’s Guide to Glide APIs

Hey there, fellow ServiceNow enthusiast! Ever felt like you’re constantly juggling data, making sure every piece of information in your instance is exactly where it needs to be and saying what it should? If you’re nodding along, you’re in good company. At the heart of a dynamic and efficient ServiceNow instance lies the ability to not just create, but also to intelligently update existing records. This isn’t just about changing a field value; it’s about automating processes, ensuring data integrity, and building truly intelligent workflows.

Today, we’re going to embark on a deep dive into the foundational magic that makes this all possible: the Glide APIs. Think of this as your essential toolkit for manipulating data and driving automation within ServiceNow. We’ll explore how to gracefully and powerfully update records, whether you’re working behind the scenes on the server or enhancing the user experience directly on the form. So, grab your coffee, roll up your sleeves, and let’s get scripting!

The Power Behind the Platform: A Glide API Overview

ServiceNow isn’t just a platform; it’s a developer’s playground where customization reigns supreme. And the key to unlocking that customization? The mighty Glide APIs. These aren’t just a collection of random functions; they’re a carefully designed set of JavaScript classes that allow us to interact with the ServiceNow database and its applications without ever having to write a single line of raw SQL. This abstraction is a lifesaver, providing flexibility, security, and a consistent way to build solutions.

Imagine trying to talk directly to a database, crafting complex SQL queries for every little change. It’d be cumbersome, error-prone, and a security nightmare. Glide APIs abstract all that away, letting you speak in JavaScript, and the platform translates your commands into efficient database operations. Each API comes packed with numerous methods, each designed to perform specific operations, from fetching data to, yes, updating those crucial records.

Types of Glide APIs: Knowing Your Battlefield

Before we dive into the nitty-gritty of updates, it’s vital to understand the two main categories of Glide APIs, as they dictate where and how you’ll use them:

Server-Side APIs: The Backend Workhorses

These APIs run directly on the ServiceNow server, executing behind the scenes. They’re perfect for complex data manipulations, integrations, business logic, and anything that doesn’t directly interact with the user’s browser.

  • GlideRecord: Our star for today! The primary API for database CRUD operations.
  • GlideSystem (gs): Provides utility functions like logging, date/time operations, and session information.
  • GlideDate/GlideDateTime: For precise date and time calculations.
  • GlideAggregation: For advanced data aggregation.
  • GlideElement: To interact with individual field elements.
  • … and many more!

Client-Side APIs: Shaping the User Experience

These APIs execute directly in the user’s web browser. They’re all about enhancing the user interface, validating input, and dynamically adjusting forms to create a seamless experience.

  • GlideForm (g_form): Our other star for client-side record manipulation.
  • GlideUser (g_user): Access user-specific information.
  • GlideAjax: For asynchronous server communication from the client.
  • GlideDialogWindow: For creating custom pop-up windows.
  • … and others like GlideList, GlideMenu.

GlideRecord: Your Go-To for Server-Side Record Updates

When it comes to server-side scripting in ServiceNow, GlideRecord is king. Seriously, if you’re writing server-side code that touches the database, you’re almost certainly using GlideRecord. It’s a special JavaScript class that allows you to perform CRUD (Create, Read, Update, Delete) operations without ever touching SQL. Why? Because direct SQL interaction is a no-go in ServiceNow for security and architectural reasons. GlideRecord acts as your secure, compliant intermediary.

Think of a GlideRecord object as a pointer to a specific table in your ServiceNow database, or even to a specific row (record) within that table. It lets you fetch data, set values, and then save those changes back to the database.

Crucial Technical Note: Test, Test, and Test Again!

Before you even think about deploying any GlideRecord script to a production environment, you absolutely, positively must test it thoroughly on a non-production instance (like your Dev or Test environment). Why the stern warning? Because an incorrectly constructed query or a typo in a field name can lead to catastrophic data loss, especially when `update()`, `updateMultiple()`, `deleteRecord()`, or `deleteMultiple()` methods are involved. You don’t want to accidentally wipe out half your incident records because of a misplaced character!

Key Characteristics of GlideRecord:

  1. The Most Common API: You’ll encounter it everywhere in Business Rules, Script Includes, Fix Scripts, and more.
  2. Server-Side Execution: It runs on the server, ensuring robust and secure data handling.
  3. Generates SQL Queries: Behind the scenes, it translates your JavaScript into optimized SQL.
  4. Performs CRUD Operations: Your one-stop shop for creating, reading, updating, and deleting records.

GlideRecord Architecture (The “How It Works” Bit)

The beauty of GlideRecord lies in its abstraction. You, the developer, write clean, readable JavaScript code like `inc.addQuery(‘priority’, ‘1’);`. ServiceNow then takes that JavaScript, translates it into the appropriate SQL query (e.g., `SELECT * FROM incident WHERE priority = 1;`), executes it against the underlying database, and returns the results to you as another GlideRecord object that you can iterate through.

Essential GlideRecord Methods: Your Scripting Arsenal

Let’s break down some of the most critical GlideRecord methods. While the list can be extensive, understanding these core ones will empower you significantly.

Testing Your Scripts: The Script – Background Application

For quickly testing GlideRecord scripts (and other server-side JavaScript), the “Script – Background” application is your best friend. Just navigate to System Definition > Scripts - Background in your instance, paste your code, and hit “Run.” It’s an isolated environment perfect for experimentation without affecting live forms or workflows.

GlideRecord in Action: Practical Exercises and Real-World Scenarios

Let’s get our hands dirty with some code. We’ll walk through various exercises, providing context and explaining the “why” behind each method, with a keen focus on how these lead up to and facilitate record updates.

Basic Output and Arithmetic

Before manipulating records, let’s ensure we know how to see our results.


// Exercise 1: How to get result (output) in ServiceNow?
// gs.print() is great for simple output in Script - Background.
// gs.info() logs to the system logs, useful for debugging scripts in production.
gs.print('Welcome to ServiceNow Academy');
gs.info('Welcome to ServiceNow Academy');
// Result: Will output "Welcome to ServiceNow Academy" in Script - Background.

// Exercise 2: Write a simple program to add two numbers
var a = 10;
var b = 20;
var c = a + b; // This line is not actually printed in the original example.
gs.print(a + b); // Simpler for direct calculation.
gs.print('The sum of a and b is: ' + c); // More descriptive output.
// Result: 30 (or "The sum of a and b is: 30")
    

Querying Records: Finding What You Need

Before you can update a record, you first need to find it! These methods are crucial for fetching the correct data.


// Exercise 3: Working with query() method
// This fetches ALL records from the 'incident' table. Use with caution on large tables!
var inc = new GlideRecord('incident'); // Initialize GlideRecord for the 'incident' table
inc.query(); // Execute the query (no conditions means all records)
while (inc.next()) { // Iterate through each record found
    gs.print(inc.number + ' - ' + inc.short_description); // Print the incident number and short description
}
// Result: Prints the number and short description for every incident in the system.

// Exercise 4: Working with query(), addQuery(), and next() methods (single condition)
// Scenario: Display all high-priority (priority = 1) incidents.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1'); // Add a condition: field 'priority' equals '1'
inc.query();
while (inc.next()) {
    gs.print('High Priority Incident: ' + inc.number);
}
// Result: Prints the numbers of all incidents with priority 1.

// Exercise 5: Passing Multiple Queries using addQuery()
// Scenario: Find active, high-priority (1), software-category incidents.
var inc = new GlideRecord('incident');
inc.addQuery('active', true);           // Query 1: Active incidents
inc.addQuery('priority', '1');          // Query 2: Priority 1
inc.addQuery('category', 'software');   // Query 3: Category 'software'
inc.query();
while (inc.next()) {
    gs.print('Matching Incident: ' + inc.number + ' - ' + inc.short_description);
}
// Result: Prints incidents that meet all three conditions.

// Exercise 6: Working with addEncodedQuery() method
// This is incredibly useful for complex queries.
// How to get an Encoded Query:
// 1. Go to any list view (e.g., Incident > All).
// 2. Apply your desired filters (e.g., Active=true AND Priority=1 AND Category=Software).
// 3. Right-click the filter breadcrumbs and choose "Copy query".
// 4. Paste it into your script!
var ecq = 'active=true^category=software^priority=1'; // Encoded query as a string
var inc = new GlideRecord('incident');
inc.addEncodedQuery(ecq); // Pass the entire encoded query string
inc.query();
while (inc.next()) {
    gs.print('Encoded Query Result: ' + inc.number);
}
// Result: Same as Exercise 5, but more concise for complex conditions.

// Exercise 7: Working with addQuery('String','Operator','Value') - Advanced Operators
// Operators: =, !=, >, >=, <, <=, IN, NOT IN, STARTSWITH, ENDSWITH, CONTAINS, DOES NOT CONTAIN, INSTANCEOF
// Scenario 1: Get Active and Priority is less than or equal to 2 (Critical/High)
var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Shorthand for addQuery('active', true)
inc.addQuery('priority', '<=', 2);
inc.query();
while (inc.next()) {
    gs.print('Priority <= 2 Incident: ' + inc.number + ' - ' + inc.priority.getDisplayValue());
}
// Result: Prints incidents with states Critical (1) or High (2).

// Scenario 2: Active, Priority <= 2, and short description contains 'SAP'
var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('priority', '<=', 2);
inc.addQuery('short_description', 'CONTAINS', 'SAP');
inc.query();
while (inc.next()) {
    gs.print('SAP Related Incident: ' + inc.number + ' | ' + inc.short_description);
}
// Result: Prints critical/high active incidents whose short description contains 'SAP'.

// Scenario 3: Incidents with category 'Software' OR 'Hardware' (using IN operator)
var categories = ['software', 'hardware'];
var inc = new GlideRecord('incident');
inc.addQuery('category', 'IN', categories); // 'IN' operator with an array of values
inc.query();
while (inc.next()) {
    gs.print('Software/Hardware Incident: ' + inc.getValue('number') + ' | ' + inc.getValue('category'));
}
// Result: Prints incidents categorized as either software or hardware.

// Scenario 4: Incidents where category STARTSWITH 'net' (e.g., Network)
var inc = new GlideRecord('incident');
inc.addQuery('category', 'STARTSWITH', 'net');
inc.query();
while (inc.next()) {
    gs.print('Network Related Incident: ' + inc.number);
}
// Result: Prints incidents with categories like 'Network', 'Networking', etc.
    

Advanced Querying and Record Navigation

These methods help you refine your results, order them, and retrieve specific records or data points.


// Exercise 8: Working with addActiveQuery() and addInactiveQuery()
// addActiveQuery(): Retrieves only active records (active = true).
var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('priority', 1);
inc.query();
while (inc.next()) {
    gs.info('Active Priority 1 Incident: ' + inc.number);
}
// Result: Prints active incidents with priority 1.

// addInactiveQuery(): Retrieves only inactive records (active = false).
var inc = new GlideRecord('incident');
inc.addInactiveQuery();
inc.addQuery('priority', 1); // This condition might not make sense for *all* inactive, priority 1.
inc.query();
while (inc.next()) {
    gs.print('Inactive Priority 1 Incident: ' + inc.number + ' (' + inc.incident_state.getDisplayValue() + ')');
}
// Result: Prints inactive incidents (often closed/resolved) that had priority 1.

// Exercise 9: Working with getEncodedQuery() method
// Useful for debugging or dynamically building queries.
var inc = new GlideRecord('incident');
inc.addEncodedQuery('active=true^category=software^priority=1');
inc.query(); // The query needs to be run once for getEncodedQuery to reflect it properly.
if (inc.next()) { // Only need to call once, as the query string is for the whole set.
    gs.print('The encoded query used was: ' + inc.getEncodedQuery());
}
// Result: Prints the encoded query string: "active=true^category=software^priority=1"

// Exercise 10: Working with orderBy() and orderByDesc()
// Ordering your results makes them more readable and predictable.
// orderBy(): Ascending order.
var inc = new GlideRecord('incident');
inc.addQuery('priority', 1);
inc.addQuery('category', 'software');
inc.orderBy('short_description'); // Order by short description alphabetically
inc.query();
while (inc.next()) {
    gs.print('Ordered Incident (Asc): ' + inc.number + ' | ' + inc.short_description);
}
// Result: Prints incidents sorted by short description in ascending order.

// orderByDesc(): Descending order.
var inc = new GlideRecord('incident');
inc.addQuery('priority', 1);
inc.addQuery('category', 'software');
inc.orderByDesc('short_description'); // Order by short description reverse-alphabetically
inc.query();
while (inc.next()) {
    gs.print('Ordered Incident (Desc): ' + inc.number + ' | ' + inc.short_description);
}
// Result: Prints incidents sorted by short description in descending order.

// Exercise 11: Working with setLimit() method
// Scenario: Get only the 10 most recent high-priority incidents.
var inc = new GlideRecord('incident');
inc.addQuery('priority', 1);
inc.orderByDesc('sys_created_on'); // Often used with orderByDesc to get "latest" records
inc.setLimit(10); // Limit the results to 10 records
inc.query();
while (inc.next()) {
    gs.print('Limited Incident: ' + inc.number + ' | ' + inc.short_description);
}
// Result: Prints only the first 10 incidents found, ordered by creation date descending.

// Exercise 12: Working with get() method
// get() is for retrieving a *single* specific record.
// Scenario 1: Get a record by its number.
var inc = new GlideRecord('incident');
inc.get('number', 'INC0009005'); // Assuming this incident number exists
gs.print('Sys_ID for INC0009005: ' + inc.sys_id);
// Result: Prints the sys_id of incident INC0009005.

// Scenario 2: Get a record by its sys_id.
var inc = new GlideRecord('incident');
inc.get('a9d7008587373010c2269c78cebb355f'); // Pass the sys_id directly
// Or more explicitly: inc.get('sys_id', 'a9d7008587373010c2269c78cebb355f');
gs.print('Incident Number for sys_id: ' + inc.number);
// Result: Prints the incident number corresponding to the given sys_id.

// Exercise 13: Working with chooseWindow() method
// Scenario: Display records from a specific range within the query results.
var inc = new GlideRecord('incident');
inc.addQuery('priority', 1);
inc.addActiveQuery();
inc.orderBy('number'); // Important to order for predictable window results
inc.chooseWindow(3, 7); // Include 3rd record, exclude 7th record (so records 3, 4, 5, 6)
inc.query();
while (inc.next()) {
    gs.print('Windowed Incident: ' + inc.number);
}
// Result: Prints a specific range of incident numbers (e.g., 4 incidents).

// Exercise 14: Working with getRowCount() method
// Scenario 1: Count all incidents.
var inc = new GlideRecord('incident');
inc.query();
gs.print('Total Incidents: ' + inc.getRowCount());
// Result: Prints the total number of records in the incident table.

// Scenario 2: Count all active users.
var userGR = new GlideRecord('sys_user');
userGR.addQuery('active', true);
userGR.query();
gs.print('Number of Active Users: ' + userGR.getRowCount());
// Result: Prints the count of active users.

// Exercise 15: Working with getTableName() method
var gr = new GlideRecord('change_request');
gs.print('Current Table Name: ' + gr.getTableName());
// Result: Displays "change_request"

// Exercise 16: Working with getValue() and getDisplayValue() methods
// getValue(): Gets the raw, stored value of a field (e.g., sys_id for a reference field, integer for a choice field).
var inc = new GlideRecord('incident');
inc.addQuery('active', true);
inc.setLimit(1);
inc.query();
if (inc.next()) {
    gs.print('Short Description (Value): ' + inc.getValue('short_description'));
    gs.print('State (Value - usually integer): ' + inc.getValue('state'));
    // getDisplayValue(): Gets the user-friendly, displayed value (e.g., "Resolved" instead of "6").
    gs.print('Priority (Display Value): ' + inc.priority.getDisplayValue());
    gs.print('State (Display Value): ' + inc.state.getDisplayValue());
}
// Result: Prints raw and display values for specified fields.

// Exercise 17: Working with hasNext() method
// Checks if there are more records to process after the current one.
var inc = new GlideRecord('incident');
inc.setLimit(1); // Ensure there's at least one record for hasNext to be true initially.
inc.query();
gs.print('Does the query have more elements? ' + inc.hasNext());
// Result: Prints "true" if there's at least one record, "false" otherwise.

// Exercise 18: Working with getUniqueValue() method
// Retrieves the unique identifier (sys_id) of the current record.
var inc = new GlideRecord('incident');
inc.query();
inc.next(); // Move to the first record
var uniqueValue = inc.getUniqueValue();
gs.print('Unique ID (sys_id) of first record: ' + uniqueValue);
// Result: Prints the sys_id of the first record in the query.

// Exercise 19: Working with setValue() and getElement() methods
// setValue(): Sets the value of a specific field.
// getElement(): Retrieves the GlideElement object for a field, allowing access to its properties.
var inc = new GlideRecord('incident');
inc.initialize(); // Prepare a new record
inc.setValue('category', 'network');
inc.setValue('short_description', 'Critical VPN Issue - Test');
inc.insert(); // Create the new record
gs.print('New Incident Category: ' + inc.category + ' | Short Description: ' + inc.short_description);
// getElement() example:
var shortDescElement = inc.getElement('short_description');
gs.print('Value from getElement: ' + shortDescElement.toString()); // .toString() gets the value
// Result: Creates a new incident with specified values and prints them.

// Exercise 20: Working with getRecordClassName() method
var gr = new GlideRecord('change_request');
gs.info('Record Class Name: ' + gr.getRecordClassName());
// Result: Prints "change_request" (which is the table name).

// Exercise 21: Working with initialize() and insert() method (Record Creation)
// While our focus is on updates, creation is the first step!
var inc = new GlideRecord('incident');
inc.initialize(); // Composes a new, empty incident form in memory
inc.category = 'network'; // Directly set field values
inc.short_description = 'Firewall Issue - Automated Creation';
inc.priority = 1;
inc.insert(); // Saves the new record to the database
gs.print('New Incident created: ' + inc.number);
// Result: Creates a new incident record and prints its number.

// Exercise 22: Working with isNewRecord() and newRecord() method
// newRecord(): Prepares a GlideRecord for a new, unsaved record with a new sys_id.
var inc = new GlideRecord('incident');
inc.newRecord();
gs.info('Is this a new record? ' + inc.isNewRecord()); // isNewRecord() checks if it's new
// Result: Prints "true".

// Exercise 23: Working with isValid() and isValidField()
// isValid(): Checks if the GlideRecord object refers to a valid table.
var inc = new GlideRecord('incident');
gs.print('Is "incident" a valid table? ' + inc.isValid()); // True
var invalidGR = new GlideRecord('non_existent_table');
gs.print('Is "non_existent_table" a valid table? ' + invalidGR.isValid()); // False

// isValidField(): Checks if a specific field exists on the current table.
gs.print('Does "incident" table have a "category" field? ' + inc.isValidField('category')); // True
gs.print('Does "incident" table have a "my_custom_field" field? ' + inc.isValidField('my_custom_field')); // False
// Results: Boolean values indicating validity.

// Exercise 24: Working with getLink() method
// Retrieves a direct URL to the current record.
var gr = new GlideRecord('incident');
gr.query();
if (gr.next()) {
    // gs.getProperty('glide.servlet.uri') gets the base URL of your instance.
    // getLink(false) returns the relative URL. getLink(true) returns a more "clean" URL.
    gs.print('Link to first incident (relative): ' + gr.getLink(false));
    gs.print('Link to first incident (absolute): ' + gs.getProperty('glide.servlet.uri') + gr.getLink(false));
}
// Result: Prints a URL to an incident record.

// Exercise 25: Working with isValidRecord() method
// Checks if the current GlideRecord object actually points to an existing record.
var inc = new GlideRecord('incident');
inc.get('number', 'INC0010012'); // Get a specific incident
gs.print(inc.number + ' exists: ' + inc.isValidRecord()); // Should be true
var nonExistentInc = new GlideRecord('incident');
nonExistentInc.get('number', 'INC9999999'); // Attempt to get a non-existent incident
gs.print('INC9999999 exists: ' + nonExistentInc.isValidRecord()); // Should be false
// Result: Boolean values indicating if a record was found.

// Exercise 26: Working with addNullQuery() and addNotNullQuery()
// addNullQuery(): Finds records where a specific field is empty/null.
var inc = new GlideRecord('incident');
inc.addNullQuery('short_description'); // Find incidents with no short description
inc.query();
while (inc.next()) {
    gs.print('Incident with Null Short Description: ' + inc.number);
}
// Result: Prints incidents lacking a short description.

// addNotNullQuery(): Finds records where a specific field has a value.
var inc = new GlideRecord('incident');
inc.addNotNullQuery('short_description'); // Find incidents that DO have a short description
inc.query();
while (inc.next()) {
    gs.print('Incident with Short Description: ' + inc.number);
}
// Result: Prints incidents that have a short description.
    

The Heart of the Matter: Updating Records with GlideRecord

Now, for what you came for! These are the methods directly responsible for modifying existing data.


// Exercise 27: Working with update() method (single record)
// Scenario: Update the state of a specific incident.
var inc = new GlideRecord('incident');
// First, find the record you want to update.
if (inc.get('number', 'INC0000057')) { // Replace with an existing incident number
    inc.setValue('state', 2); // Set state to 'In Progress' (assuming 2 is the value for In Progress)
    inc.update(); // Save the changes to the database
    gs.print('Incident ' + inc.number + ' updated to state: ' + inc.state.getDisplayValue());
} else {
    gs.print('Incident INC0000057 not found.');
}
// Result: The specified incident's state will be updated.

// Exercise 28: Working with updateMultiple() method (multiple records)
// Scenario: Change the category for all 'hardware' incidents to 'software'.
var inc = new GlideRecord('incident');
inc.addQuery('category', 'hardware'); // Find all incidents with category 'hardware'
inc.setValue('category', 'software'); // Set the new category
inc.updateMultiple(); // Apply the change to all records found by the query
gs.print('All hardware category incidents updated to software.');
// Result: Multiple incident records will have their category updated.
// IMPORTANT: Use updateMultiple() with extreme caution. Always test on non-prod first!
    

Deleting Records (Use with Extreme Caution!)

While not "updating," these methods are part of the CRUD operations and complete the picture.


// Exercise 29: Working with deleteRecord() method (single record)
// Scenario: Delete a specific incident.
var inc = new GlideRecord('incident');
if (inc.get('number', 'INC0010013')) { // Replace with a specific incident number you wish to delete
    inc.deleteRecord(); // Delete the record
    gs.print('Incident ' + inc.number + ' deleted.');
} else {
    gs.print('Incident INC0010013 not found for deletion.');
}
// Result: The specified incident will be permanently removed.

// Exercise 30: Working with deleteMultiple() method (multiple records)
// Scenario: Delete all incidents with priority 4.
var inc = new GlideRecord('incident');
inc.addQuery('priority', 4); // Find all incidents with priority 4
inc.deleteMultiple(); // Delete all records found by the query
gs.print('All incidents with priority 4 deleted.');
// Result: Multiple incident records will be permanently removed.
// DANGER: deleteMultiple() is irreversible. Use with maximum caution!
    

Access Control Methods: Checking Permissions

These methods help you programmatically check if the current user has the necessary permissions based on ACLs (Access Control Lists).


// Exercise 31: Working with canCreate(), canRead(), canWrite(), canDelete()
var inc = new GlideRecord('incident');
gs.print('Can current user create incident records? ' + inc.canCreate());
gs.print('Can current user read incident records? ' + inc.canRead());
gs.print('Can current user write to incident records? ' + inc.canWrite());
gs.print('Can current user delete incident records? ' + inc.canDelete());
// Result: Returns true/false based on the current user's roles and ACLs for the incident table.
    

Workflow and System Field Control: The "Silent" Update

Sometimes you need to update a record without triggering all the usual platform machinery.


// Exercise 32: Working with autoSysFields() and setWorkflow() methods
// Scenario: Update multiple records without modifying 'sys_updated_by', 'sys_updated_on', etc.,
// and without triggering business rules or workflows.
var inc = new GlideRecord('incident');
inc.addQuery('state', 1); // Query for incidents in state 'New'
inc.setLimit(5); // Limit for testing, remove in production if needed
inc.query();
while (inc.next()) {
    inc.autoSysFields(false); // Disable updates to system audit fields (sys_updated_on, sys_updated_by, etc.)
    inc.setWorkflow(false);   // Disable running business rules, workflows, and other engine processes
    inc.setValue('state', 2); // Set state to 'In Progress'
    inc.update();
    gs.print('Incident ' + inc.number + ' silently updated to In Progress.');
}
// Result: Records are updated, but their "Last Updated" information remains unchanged, and no
// associated business rules or workflows are triggered by this script.
// Note: autoSysFields(false) might not work in all scoped applications.
// Note: setWorkflow(false) is powerful but can lead to unexpected behavior if you bypass critical logic.
    

Advanced Join Queries and Abort Actions


// Exercise 33: Working with addJoinQuery() method
// Scenario: Find all problems that have associated incidents where the problem's opened_by matches the incident's caller_id.
var prob = new GlideRecord('problem');
prob.addJoinQuery('incident', 'opened_by', 'caller_id'); // Join 'problem' to 'incident'
prob.query();
while (prob.next()) {
    gs.print('Problem ' + prob.number + ' has an associated incident.');
}
// Result: Displays numbers of problem records that meet the join condition.

// Exercise 34: Working with getGlideObject(), getNumericValue(), and setAbortAction()
// This is typically used in Business Rules for validation.
// Scenario: Prevent an update/insert if a start date is after an end date.
// Assume 'current.u_date1' and 'current.u_date2' are DateTime fields on the current record.
// This code would be placed in an 'onBefore' Business Rule.
if ((!current.u_date1.nil()) && (!current.u_date2.nil())) {
    var start = current.u_date1.getGlideObject().getNumericValue(); // Convert DateTime to milliseconds
    var end = current.u_date2.getGlideObject().getNumericValue();
    if (start > end) {
        gs.addInfoMessage('Start date must be before end date.');
        current.u_date1.setError('Start date must be before end date.'); // Highlight the field
        current.setAbortAction(true); // Prevents the current record action (insert/update) from completing
    }
}
// Result: If the condition (start date > end date) is met, the update/insert is blocked,
// an info message is displayed, and the start date field is highlighted with an error.
    

GlideForm: Client-Side Form Interaction for Updates

While GlideRecord handles the heavy lifting on the server, sometimes you need to influence data on the form itself, directly in the user's browser. That's where GlideForm (g_form) comes in. It's your client-side powerhouse for manipulating form fields, setting values, showing messages, and controlling visibility, all before the record is even submitted for a server-side update.

GlideForm Overview and Usage (g_form)

  • Client-Side API: Exclusively runs in the browser.
  • Global Object: Accessed via the global g_form object.
  • Form View Customization: Methods are used to make dynamic changes to the form.
  • Client Scripts & Catalog Client Scripts: Most frequently used in these script types.

GlideForm in Action: Practical Exercises

To test these methods, you'll typically be working within a Client Script configured for a specific table (e.g., Incident). For quick console testing:

  1. Navigate to an existing record (e.g., an Incident).
  2. Open your browser's developer console (usually F12 or Ctrl+Shift+J).
  3. Go to the 'Console' tab.
  4. Type and run your g_form code directly.

// Exercise 1: Working with getValue() method
// Retrieves the current value of a field on the form.
alert(g_form.getValue('category'));
// Result: A browser alert box showing the current category of the incident.

// Exercise 2: Working with setValue() method
// Sets the value of a specific field on the form. This is a primary way to initiate data changes client-side.
// Often used in onChange scripts: if (g_form.getValue('category') == 'Software') { g_form.setValue('subcategory', 'Application'); }
g_form.setValue('category', 'hardware');
alert(g_form.getValue('category')); // Verify the change
// Result: The 'Category' field on the form will change to 'Hardware'.

// Exercise 3: Working with setDisabled() method
// Makes a field read-only on the form.
g_form.setDisabled('category', true);
// Result: The 'Category' field will become uneditable.
// Note: For persistent read-only states, UI Policies or ACLs are generally best practice.

// Exercise 4: Working with setMandatory() method
// Makes a field mandatory (adds a red asterisk) on the form.
g_form.setMandatory('category', true);
// Result: The 'Category' field will visually become mandatory.
// Note: UI Policies are typically preferred for making fields mandatory conditionally.

// Exercise 5: Working with setDisplay() method
// Hides or shows a specific field, removing its space from the form layout.
g_form.setDisplay('business_service', false);
// Result: The 'Business Service' field will disappear entirely from the form.
// Note: UI Policies are often better for managing field visibility.

// Exercise 6: Working with setVisible() method
// Hides or shows a field, but maintains its occupied space on the form layout.
g_form.setVisible('subcategory', false);
// Result: The 'Subcategory' field will be hidden, but an empty space will remain where it was.

// Exercise 7: Working with isMandatory() method
// Checks if a field is currently set as mandatory on the form.
alert(g_form.isMandatory('category'));
// Result: A browser alert showing 'true' or 'false'.
    

Troubleshooting Tips for ServiceNow Scripting

Even the most seasoned developers hit snags. Here are some universal troubleshooting tips for scripting in ServiceNow:

  • Check the Logs: For server-side scripts, gs.info() and gs.error() are your friends. Check System Logs > All or System Logs > Script Log Statements.
  • Script - Background: Test small, isolated chunks of server-side code here.
  • Browser Console: For client-side scripts, use console.log() statements and the browser's developer tools (F12).
  • Line-by-Line Debugging: If available in your instance (usually through the Debugger role), use the server-side debugger. For client-side, browser developer tools offer excellent breakpoint debugging.
  • Check Field Names: Typos in field names (e.g., inc.shortdescription instead of inc.short_description) are a common culprit. Double-check column names.
  • Query Validation: For GlideRecord queries, test the encoded query in a list view first to ensure it returns the expected records.
  • ACLs & Permissions: If a script isn't creating, reading, updating, or deleting as expected, it might be an ACL issue. Test with a user who has admin or security_admin to rule this out.
  • Order of Operations: Understand when your script runs (e.g., Before/After Business Rules, onCellEdit/onChange/onLoad/onSubmit Client Scripts).
  • Scoped vs. Global: Be aware of differences, especially with methods like autoSysFields(), when working in scoped applications.

Interview Relevance: Talking the Talk

Understanding GlideRecord and GlideForm isn't just about writing code; it's about demonstrating your fundamental knowledge of ServiceNow development. In an interview, be prepared to:

  • Explain the Difference: Clearly articulate when to use GlideRecord (server-side, database operations, automation) versus GlideForm (client-side, UI/UX, real-time form manipulation).
  • Describe CRUD Operations: Walk through how to Create (`initialize()`, `insert()`), Read (`query()`, `next()`, `get()`), Update (`setValue()`, `update()`, `updateMultiple()`), and Delete (`deleteRecord()`, `deleteMultiple()`) using GlideRecord.
  • Discuss Best Practices: Mention when to use UI Policies over Client Scripts for form UI, the importance of `setWorkflow(false)` and `autoSysFields(false)` for specific scenarios, and the critical need for testing.
  • Provide Real-World Scenarios: Be ready to give examples of when you'd use an `addEncodedQuery`, `orderByDesc()`, or `setLimit()`. For GlideForm, explain how `setValue()` might dynamically update a field based on user input.
  • Troubleshooting: Show that you know how to debug issues in both server-side (logs, Script - Background) and client-side (browser console) environments.

By mastering these core concepts and being able to articulate them clearly, you'll not only be a more effective ServiceNow developer but also a highly sought-after one!

Wrapping Up

Phew! We've covered a lot of ground today, from the foundational principles of Glide APIs to the nitty-gritty details of updating records using GlideRecord on the server and GlideForm on the client. Understanding these mechanisms is non-negotiable for anyone serious about ServiceNow development. You're not just changing data; you're orchestrating the very flow of information and functionality within your instance.

Remember, practice makes perfect. Spin up your personal developer instance, experiment with these methods, break things (in dev, please!), and learn from every error. The more comfortable you become with GlideRecord and GlideForm, the more powerful and elegant your ServiceNow solutions will be. Happy scripting!


Scroll to Top