Mastering GlideRecord API Mapping for ServiceNow Development

Unlocking ServiceNow’s Database Power: A Deep Dive into GlideRecord API Mapping

Hey there, fellow ServiceNow enthusiast! Ever wondered how those slick scripts you write effortlessly interact with the underlying database without a single SQL query in sight? Or perhaps you’re looking to supercharge your platform customizations and break free from default behaviors? You’ve come to the right place.

Today, we’re diving headfirst into one of ServiceNow’s most fundamental and powerful server-side APIs: GlideRecord. Think of it as your personal translator and gatekeeper to the ServiceNow database, allowing you to perform complex operations with elegant JavaScript. We’ll explore its architecture, map its methods to familiar database actions, and arm you with practical examples to confidently wield this critical tool.

The Heart of ServiceNow Scripting: Glide APIs Overview

If you’ve spent any time developing on the ServiceNow platform, you know that customization is key. Whether it’s streamlining workflows, integrating with external systems, or enhancing user experiences, Glide APIs are the secret sauce. These powerful classes provide a flexible, programmatic way to interact with your ServiceNow instance, allowing you to manipulate data, control UI elements, and much more, all without resorting to direct SQL.

The beauty of Glide APIs is that they abstract away the complexities of the platform’s backend, letting you focus on the logic. Each API comes packed with a treasure trove of methods, each designed to perform specific operations, from fetching user details to creating new records or altering form fields.

Client-Side vs. Server-Side: A Quick Distinction

Before we zero in on GlideRecord, it’s crucial to understand the two main theaters of operation for Glide APIs: client-side and server-side. This distinction determines where your code runs and what kind of interactions it can perform.

Client-SideServer-Side
GlideForm (Manipulate forms, fields)GlideRecord (Database CRUD operations)
GlideUser (Access user information)GlideSystem (Log messages, retrieve properties, utilities)
GlideAjax (Asynchronous calls to server)GlideDate (Date manipulation)
GlideDialogWindow (Create pop-up dialogs)GlideDateTime (Date and time manipulation)
GlideList (Interact with list views)GlideAggregation (Aggregate data)
GlideMenu (Customize context menus)GlideElement (Access field properties)

As you can see, our star of the show, GlideRecord, lives firmly on the server-side. This means it runs on the ServiceNow instance itself, giving it direct and secure access to the database.

Demystifying GlideRecord: Your Database Buddy

If you ask any seasoned ServiceNow developer about the most frequently used and indispensable API, GlideRecord will undoubtedly top their list. It’s not just common; it’s the cornerstone for server-side data manipulation.

At its core, GlideRecord is a special JavaScript class (though technically a native Java class wrapped for JavaScript execution) designed to perform standard database operations – think Create, Read, Update, and Delete (CRUD) – but with a ServiceNow twist. Instead of writing complex SQL queries, you interact with your tables and their rows and columns using intuitive JavaScript methods. This abstraction layer is a game-changer, making database interactions more secure, more manageable, and significantly easier for developers.

GlideRecord in a Nutshell:

  • Most Common & Important API: It’s the go-to for server-side data interactions.
  • Server-Side Execution: All GlideRecord operations happen on the ServiceNow server.
  • SQL Generator: It translates your JavaScript methods into the appropriate SQL queries behind the scenes.
  • CRUD Operations: Your primary tool for creating, reading, updating, and deleting records.
  • Database Interaction: It manages rows and columns in the underlying database without direct SQL access.

A Crucial Caveat: Test Before You Fly!

Before we dive into the nitty-gritty of methods, a word of caution that cannot be overstated: Always, always, ALWAYS test your GlideRecord scripts on a non-production instance first! An incorrectly constructed query, an invalid field name, or a logical error in your script can have severe consequences, potentially leading to unintended data changes or even data loss in a production environment. Imagine running a `deleteRecord()` or `updateMultiple()` with a faulty query – it could be disastrous! So, practice safe scripting: develop in your Personal Developer Instance (PDI) or a dedicated development environment, test thoroughly, and then deploy.

Peeking Under the Hood: GlideRecord Architecture

Understanding how GlideRecord works beneath the surface helps solidify its importance. Picture this: you write a few lines of JavaScript using GlideRecord methods. What happens next?

Instead of you painstakingly crafting SQL queries, your JavaScript code communicates with the GlideRecord API. GlideRecord then acts as an intelligent interpreter, translating your high-level JavaScript commands into the precise SQL statements needed to interact with the database. This SQL is then executed against the ServiceNow database, and the results are returned back through GlideRecord to your JavaScript. This architectural pattern keeps your code clean, readable, and insulated from the nuances of direct database interaction, enhancing both security and developer efficiency.

Conceptually, this is what “GlideRecord API Mapping” refers to: the seamless translation of your JavaScript methods into underlying database operations. For example:

  • A GlideRecord query() method maps to a SQL SELECT statement.
  • An insert() method maps to a SQL INSERT statement.
  • An update() method maps to a SQL UPDATE statement.
  • A deleteRecord() or deleteMultiple() method maps to a SQL DELETE statement.

This abstraction is precisely why GlideRecord is so powerful and widely adopted.

Navigating the Methods: Your GlideRecord Toolkit

Now for the exciting part – getting hands-on with the methods! We’ll explore a comprehensive list of GlideRecord methods, providing practical examples to illustrate their usage. Remember, you can test all these snippets in the “Scripts – Background” application in your ServiceNow instance.

Basic Output and Operations

gs.print() and gs.info(): Your Debugging Companions

These methods, part of the GlideSystem API, are indispensable for seeing the output of your scripts. gs.print() logs messages to the script output, while gs.info() logs them to the system logs (useful for debugging scheduled jobs or business rules). Using both often helps cover your bases.

gs.print('Welcome to ServiceNow Academy');
gs.info('Welcome to ServiceNow Academy');

Result: Welcome to ServiceNow Academy (in both script output and system logs)

Basic Arithmetic: Scripting Fundamentals

Just like any JavaScript environment, you can perform basic operations. This is a quick refresher.

var a = 10;
var b = 20;
var c = a + b;
gs.print(a + b);

Result: 30

Mastering the Art of Querying

Querying is the bread and butter of GlideRecord. It’s how you retrieve the data you need to work with.

query() and next(): The Foundation of Data Retrieval

The query() method executes the query defined by any addQuery() or similar methods you’ve chained. The while(inc.next()) loop then iterates through each record found by that query. This pattern is fundamental.

var inc = new GlideRecord('incident'); // Create a GlideRecord object for the 'incident' table
inc.query(); // Execute the query (without any specific conditions, it retrieves all records)
while (inc.next()) { // Loop through each incident record found
    gs.print(inc.number); // Print the 'number' field of the current incident
}

Result: Prints the numbers of all incident records in the system.

addQuery('field', 'value'): Filtering Your Results

This method allows you to add specific conditions to your query, narrowing down the results. It’s like adding a WHERE clause in SQL.

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1'); // Add a condition: priority equals 1
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints the numbers of all incidents where the priority is ‘1’ (Critical).

Working with Multiple Queries: Chaining Conditions

You can chain multiple addQuery() methods. By default, these conditions are combined with an AND operator, meaning all conditions must be true for a record to be returned.

var inc = new GlideRecord('incident');
inc.addQuery('active', true); // Condition 1: active is true
inc.addQuery('priority', '1'); // Condition 2: priority is 1
inc.addQuery('category', 'software'); // Condition 3: category is 'software'
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers for records that are active, have priority 1, AND are in the ‘software’ category.

addEncodedQuery('encoded_query_string'): The Power of Pre-built Queries

Encoded queries are powerful strings that represent complex conditions. You can generate these by building a filter in any list view, right-clicking the breadcrumbs, and selecting “Copy query.” This is incredibly useful for complex or dynamic queries.

var inc = new GlideRecord('incident');
inc.addEncodedQuery('active=true^category=software^priority=1'); // Directly apply a complex encoded query
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers matching the specified active, category, and priority conditions.

You can also store the encoded query in a variable for better readability or reusability:

var ecq = 'active=true^category=software^priority=1'; // Encoded query stored in a variable
var inc = new GlideRecord('incident');
inc.addEncodedQuery(ecq);
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Same as above, demonstrating variable usage.

Advanced Query Techniques & Operators

GlideRecord allows for flexible querying using various operators, just like in SQL. The syntax is addQuery('field_name', 'operator', 'value').

SQL Operators for addQuery():

  • = (equals)
  • != (not equals)
  • > (greater than)
  • >= (greater than or equals)
  • < (less than)
  • <= (less than or equals)

String Operators (must be in upper case):

  • IN (value is in a list)
  • NOT IN (value is not in a list)
  • STARTSWITH (field value starts with a string)
  • ENDSWITH (field value ends with a string)
  • CONTAINS (field value contains a string)
  • DOES NOT CONTAIN (field value does not contain a string)
  • INSTANCEOF (used for hierarchical tables)

addActiveQuery(): Simplifying “Active = True”

A convenient shortcut for adding the `active=true` condition to your query.

var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Adds 'active=true' condition
inc.addQuery('priority', '<=', 2); // Condition: priority is less than or equal to 2
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers for active records with Priority 1 (Critical) or Priority 2 (High).

Combining Operators: Complex Filtering

You can combine different operators to create very specific filters.

var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Active incidents
inc.addQuery('priority', '<=', 2); // Priority 1 or 2
inc.addQuery('short_description', 'CONTAINS', 'SAP'); // Short description contains 'SAP'
inc.query();
while (inc.next()) {
    gs.print(inc.number + ' ' + inc.short_description);
}

Result: Prints active incidents with priority 1 or 2, whose short description contains “SAP”.

IN Operator: Matching Against a List

Useful when you need to check if a field’s value is among a predefined set of values.

var cat = ['software', 'hardware']; // Array of categories to search for
var inc = new GlideRecord('incident');
inc.addQuery('category', 'IN', cat); // Category is 'software' OR 'hardware'
inc.query();
while (inc.next()) {
    gs.print(inc.getValue('number') + ' ' + inc.getValue('short_description'));
}

Result: Prints incidents where the category is either ‘software’ or ‘hardware’.

STARTSWITH Operator: Pattern Matching

Finds records where a field’s value begins with a specific string.

var inc = new GlideRecord('incident');
inc.addQuery('category', 'STARTSWITH', 'net'); // Category starts with 'net' (e.g., network)
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers where the category starts with “net”.

addInactiveQuery(): Finding Closed or Inactive Records

The inverse of addActiveQuery(), this adds the `active=false` condition.

var inc = new GlideRecord('incident');
inc.addInactiveQuery(); // Adds 'active=false' condition
inc.addQuery('priority', '1');
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers for inactive (e.g., closed) records with priority 1.

getEncodedQuery(): Inspecting Your Query

Returns the encoded query string that represents all the conditions you’ve built with addQuery(), addEncodedQuery(), etc. Great for debugging or dynamic query building.

var inc = new GlideRecord('incident');
inc.addEncodedQuery('active=true^category=software^priority=1');
inc.query(); // You need to query first to have the query string built internally
while (inc.next()) { // The loop is not strictly necessary for getEncodedQuery, but it shows context
    gs.print(inc.getEncodedQuery());
    break; // Only print it once
}

Result: Prints the encoded query string: active=true^category=software^priority=1.

addNullQuery() and addNotNullQuery(): Handling Empty Values

These methods help you specifically target records where a field’s value is either empty/null or not empty/null.

var inc = new GlideRecord('incident');
inc.addNullQuery('short_description'); // Find incidents with no short description
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers where the ‘short_description’ field is null or empty.

var inc = new GlideRecord('incident');
inc.addNotNullQuery('short_description'); // Find incidents with a short description
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints incident numbers where the ‘short_description’ field is not null or empty.

Controlling Record Sets

Beyond just querying, you often need to control the order and number of records returned.

orderBy('field_name') and orderByDesc('field_name'): Sorting Your Results

These methods allow you to sort the query results in ascending (orderBy) or descending (orderByDesc) order based on a specified field, much like SQL’s ORDER BY clause.

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.addQuery('category', 'software');
inc.orderBy('short_description'); // Sort results by short description in ascending order
inc.query();
while (inc.next()) {
    gs.print(inc.number + ' ' + inc.short_description);
}

Result: Prints incidents (priority 1, software category) sorted alphabetically by their short description.

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.addQuery('category', 'software');
inc.orderByDesc('short_description'); // Sort results by short description in descending order
inc.query();
while (inc.next()) {
    gs.print(inc.number + ' ' + inc.short_description);
}

Result: Prints incidents (priority 1, software category) sorted reverse-alphabetically by their short description.

setLimit(number): Limiting Your Output

Returns only a specified number of records. Useful for performance optimization or when you only need a few examples.

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.orderByDesc('sys_created_on'); // Get the newest records first
inc.setLimit(10); // Only retrieve the first 10 records
inc.query();
while (inc.next()) {
    gs.print(inc.number + ' ' + inc.short_description);
}

Result: Prints the 10 newest incidents with priority 1.

chooseWindow(offset, limit): Paginating Results

Allows you to retrieve a specific “window” of records. The first parameter is the starting offset (inclusive, 0-indexed), and the second is the ending offset (exclusive). So, `chooseWindow(3,7)` retrieves records at indexes 3, 4, 5, and 6 (4 records).

var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.addActiveQuery();
inc.chooseWindow(3, 7); // Get records from index 3 up to (but not including) index 7
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Prints records at positions 4, 5, 6, and 7 from the full query result set (based on 0-indexing).

Retrieving Record & Field Details

Once you have a GlideRecord object, you’ll want to extract information from it.

get('sys_id') or get('field_name', 'value'): Fetching a Single Record

Retrieves a single record directly. You can pass the record’s `sys_id` or a field name and its value (e.g., incident number).

var inc = new GlideRecord('incident');
inc.get('number', 'INC0009005'); // Retrieve incident with a specific number
gs.print(inc.sys_id);

Result: Prints the `sys_id` of incident INC0009005.

var inc = new GlideRecord('incident');
inc.get('26524310978720023a1027b4b453af06'); // Retrieve incident using its sys_id
gs.print(inc.number);

Result: Prints the number of the incident associated with that `sys_id`.

getRowCount(): Counting Your Records

Returns the total number of records found by the query. Very useful for dashboards or reporting.

var inc = new GlideRecord('incident');
inc.query();
gs.print(inc.getRowCount());

Result: Prints the total number of records in the incident table.

var userGr = new GlideRecord('sys_user');
userGr.addQuery('active', true);
userGr.query();
gs.print('Active users are: ' + userGr.getRowCount());

Result: Prints the count of active users.

getTableName(): Identifying the Current Table

Returns the name of the table the GlideRecord object is currently associated with.

var crGr = new GlideRecord('change_request');
gs.print(crGr.getTableName());

Result: Displays ‘change_request’.

getValue('field_name') and getDisplayValue('field_name'): Raw vs. Readable Values

getValue() returns the raw, internal value of a field (e.g., ‘1’ for Critical priority, ‘sys_id’ for a reference field). getDisplayValue() returns the user-friendly, displayed value (e.g., ‘Critical’ for priority, the name for a reference field).

var inc = new GlideRecord('incident');
inc.addQuery('active', true);
inc.setLimit(1);
inc.query();
if (inc.next()) {
    gs.print('Short description (raw): ' + inc.getValue('short_description'));
    gs.print('Short description (dot-walked): ' + inc.short_description); // Direct access also works
    gs.print('Priority (raw): ' + inc.getValue('priority'));
    gs.print('Priority (display): ' + inc.priority.getDisplayValue()); // For choice fields, use .getDisplayValue() on the element
}

Result: Prints the short description (raw), priority (raw as ‘1’, ‘2’, etc.), and priority (display value ‘Critical’, ‘High’, etc.). Note that for dot-walking, like `inc.priority.getDisplayValue()`, you’re essentially getting a GlideElement object for the field and then calling its `getDisplayValue()` method.

hasNext(): Checking for More Records

Returns `true` if there are more records to iterate through in the current query result, `false` otherwise. Often used implicitly within a `while` loop, but can be checked explicitly.

var inc = new GlideRecord('incident');
inc.setLimit(1); // Ensure there's at least one record for true, or 0 for false
inc.query();
gs.print(inc.hasNext()); // Will likely print 'true' if any incidents exist

Result: Prints ‘true’ if the query found at least one record, ‘false’ otherwise.

getUniqueValue(): Getting the Record’s Unique Identifier

Returns the unique identifier for the current record, which is almost always its `sys_id`.

var inc = new GlideRecord('incident');
inc.query();
if (inc.next()) { // Move to the first record
    var uniqvalue = inc.getUniqueValue();
    gs.print(uniqvalue);
}

Result: Prints the `sys_id` of the first incident record found.

getElement('field_name'): Getting a Field’s GlideElement Object

Returns the GlideElement object for a specified field. This allows you to access properties and methods specific to that field, like `getDisplayValue()`, `getLabel()`, `getGlideObject()`, etc.

var inc = new GlideRecord('incident');
inc.initialize();
inc.setValue('short_description', 'I am facing VPN Problem');
inc.insert();
gs.print(inc.getElement('short_description').toString()); // Get the GlideElement and convert to string
gs.print(inc.getElement('category').getLabel()); // Get the label of the category field

Result: Prints “I am facing VPN Problem” (the value of the short_description field) and then “Category” (the label of the category field).

getRecordClassName(): Getting the Table’s Class Name

Retrieves the class name of the current record’s table, which is typically the table name itself.

var crGr = new GlideRecord('change_request');
gs.info(crGr.getRecordClassName());

Result: Prints ‘change_request’ to the system logs.

getLink() and gs.getProperty(): Generating Record URLs

getLink() returns a relative URL to the current record. By combining it with gs.getProperty('glide.servlet.uri') (which gives you the base instance URL), you can construct a full URL to the record.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('category', 'software');
inc.addQuery('priority', '1');
inc.query();
if (inc.next()) { // Get the link for the first matching record
    gs.print(gs.getProperty('glide.servlet.uri') + inc.getLink(false));
}

Result: Returns a full URL to the first incident record found, like `https://yourinstance.service-now.com/nav_to_url.do?uri=incident.do?sys_id=YOUR_SYS_ID`.

Crafting & Modifying Records

This is where GlideRecord truly shines for data manipulation.

initialize(): Preparing a New Record

Prepares a new, empty record object in memory. This is your first step before setting field values for a new record.

setValue('field_name', 'value'): Setting Field Values

Sets the value of a specific field on the current GlideRecord object. An alternative to direct dot-walking (e.g., inc.category = 'network').

insert(): Saving a New Record

Commits the new record (prepared with initialize() and populated with setValue() or dot-walking) to the database.

var inc = new GlideRecord('incident');
inc.initialize(); // Compose an empty incident record
inc.setValue('category', 'network'); // Set category using setValue
inc.short_description = 'Critical VPN Issue'; // Set short description using dot-walking
inc.insert(); // Save the new record to the database
gs.print('Category is ' + inc.category + ' and ' + 'issue is: ' + inc.short_description);
gs.print('New Incident Number: ' + inc.number);

Result: Creates a new incident record with the specified category and short description, and then prints its details and number.

var inc = new GlideRecord('incident');
inc.initialize(); // Compose incident form
inc.category = 'network'; // set field values directly
inc.short_description = 'Firewall Issue';
inc.priority = 1;
inc.insert(); // create new record
gs.print(inc.number); // print new record incident number

Result: Creates another new incident record and prints its number.

isNewRecord() and newRecord(): Checking for Newness and Creating New

newRecord() creates a new GlideRecord object with default values and a unique ID, effectively starting a new record creation process. isNewRecord() checks if the current GlideRecord object represents a record that hasn’t been saved to the database yet.

var inc = new GlideRecord('incident');
inc.newRecord(); // Creates a new record object, ready for insertion
gs.info(inc.isNewRecord()); // Check if it's a new record
inc.short_description = 'Creating new record with newRecord()';
inc.category = 'software';
inc.insert(); // Insert the record
gs.print(inc.number);

Result: Prints ‘true’ (because it’s a new record before insertion), then creates a new incident and prints its number.

isValid() and isValidField(): Validating Tables and Fields

isValid() checks if the GlideRecord object is associated with a valid, existing table. isValidField() checks if a specified field exists in the current table.

var inc = new GlideRecord('incident');
gs.print('Incident table is valid: ' + inc.isValid()); // Should be true

var invalidGr = new GlideRecord('uday'); // Assuming 'uday' is not a real table
gs.print('Uday table is valid: ' + invalidGr.isValid()); // Should be false

Result: Prints ‘True’ then ‘False’.

var inc = new GlideRecord('incident');
gs.print('Category field is valid: ' + inc.isValidField('category')); // Should be true
gs.print('NonExistentField field is valid: ' + inc.isValidField('nonexistent_field')); // Should be false

Result: Prints ‘True’ then ‘False’.

isValidRecord(): Validating Query Results

Checks if the current GlideRecord object actually contains data retrieved from a query or `get()` operation. Returns true if a record was found.

var inc = new GlideRecord('incident');
inc.get('number', 'INC0010012'); // Try to get a specific incident
gs.print(inc.number + ' exists: ' + inc.isValidRecord()); // Checks if a record was successfully retrieved

var nonExistentInc = new GlideRecord('incident');
nonExistentInc.get('number', 'NONEXISTENT123');
gs.print('NONEXISTENT123 exists: ' + nonExistentInc.isValidRecord());

Result: For an existing INC number, it prints “INC0010012 exists: true”. For a non-existent one, it prints “undefined exists: false”.

update(): Modifying a Single Record

Saves changes to an existing record. You first retrieve the record, modify its field values, then call update().

var inc = new GlideRecord('incident');
inc.get('number', 'INC0000057'); // Get the specific incident to update
inc.setValue('state', '2'); // Set the state to 'Active' or another desired value
inc.update(); // Save the changes
gs.print('Incident ' + inc.number + ' updated successfully.');

Result: The incident INC0000057’s state field is updated as specified in the database.

updateMultiple(): Modifying Many Records

Updates all records that match the current query conditions with the specified field changes. This is highly efficient for bulk updates.

var inc = new GlideRecord('incident');
inc.addQuery('category', 'hardware'); // Find all incidents with category 'hardware'
inc.setValue('category', 'software'); // Change their category to 'software'
inc.updateMultiple(); // Apply the update to all matching records
gs.print('Multiple records updated from hardware to software category.');

Result: All incident records with the category ‘hardware’ are updated to ‘software’.

Deleting Records

Handle with extreme care, especially in production environments!

deleteRecord(): Deleting a Single Record

Deletes the single current record from the database. You must first retrieve the record using `get()` or a query that finds only one record.

var inc = new GlideRecord('incident');
inc.get('number', 'INC0010013'); // Retrieve the specific record to delete
if (inc.isValidRecord()) {
    inc.deleteRecord(); // Delete it
    gs.print('Incident ' + inc.number + ' deleted successfully.');
} else {
    gs.print('Incident INC0010013 not found.');
}

Result: Deletes the incident INC0010013 if it exists.

deleteMultiple(): Deleting Many Records

Deletes all records that satisfy the current query conditions. Use with utmost caution!

var inc = new GlideRecord('incident');
inc.addQuery('priority', '4'); // Find all incidents with priority 4 (Low)
inc.query();
var deletedCount = inc.getRowCount(); // Get count before deleting
inc.deleteMultiple(); // Delete all matching records
gs.print(deletedCount + ' incidents with priority 4 deleted.');

Result: All incident records with priority 4 are deleted from the system.

Access Control & Permissions

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

canCreate(), canRead(), canWrite(), canDelete(): ACL Checks

These methods return `true` if the current user (based on their roles and ACLs) is permitted to perform the respective action on the table or record, and `false` otherwise.

var inc = new GlideRecord('incident');
gs.print('Can create incident: ' + inc.canCreate());
gs.print('Can read incident: ' + inc.canRead());
gs.print('Can write incident: ' + inc.canWrite());
gs.print('Can delete incident: ' + inc.canDelete());

Result: Prints ‘True’ for each permission if the logged-in user has the necessary roles (e.g., ITIL role). If not, it will be ‘False’.

Optimizing Performance & System Behavior

These methods offer fine-grained control over how GlideRecord interacts with the platform’s underlying mechanisms.

autoSysFields(false) and setWorkflow(false): Bypassing System Updates and Business Rules

autoSysFields(false): Prevents system fields like `sys_updated_on`, `sys_updated_by`, `sys_mod_count`, `sys_created_on`, `sys_created_by` from being updated when you save a record. This is crucial for data imports or specific integrations where you want to preserve the original system audit trail. (Note: This method is generally not supported in Scoped Applications.)

setWorkflow(false): Prevents any Business Rules or workflows from running when you save a record. This can be a performance booster for bulk operations, but use with extreme caution as it bypasses critical automation.

var inc = new GlideRecord('incident');
inc.addQuery('state', '1'); // Find incidents in 'New' state
inc.query();
while (inc.next()) {
    inc.autoSysFields(false); // Do not update system fields (created, updated by/on, mod count)
    inc.setWorkflow(false); // Do not run Business Rules or workflows
    inc.setValue('state', '2'); // Set state to 'Active'
    inc.update(); // Save changes
    gs.print('Incident ' + inc.number + ' state updated without sys_fields/workflow.');
}

Result: Updates the state of all ‘New’ incidents to ‘Active’ without triggering system field updates or associated workflows/business rules.

addJoinQuery('joined_table', 'local_field', 'foreign_field'): Joining Tables

This method allows you to perform queries that involve joining two tables. It’s similar to a SQL JOIN, allowing you to filter records in the primary table based on conditions in a related table.

var prob = new GlideRecord('problem');
// Join the 'problem' table with the 'incident' table
// where 'problem.opened_by' matches 'incident.caller_id'
prob.addJoinQuery('incident', 'opened_by', 'caller_id');
prob.query();
while (prob.next()) {
    gs.print(prob.number + ' (opened by ' + prob.opened_by.name + ') has associated incidents.');
}

Result: Displays all problem records that have at least one associated incident where the problem’s ‘opened by’ user is also the incident’s ‘caller’.

getGlideObject().getNumericValue() and setAbortAction(true): Advanced Field Handling and Aborting Actions

getGlideObject(): Returns the underlying Java object for a GlideElement, providing access to more advanced methods, like `getNumericValue()` for date/time fields.
setAbortAction(true): This is often used in Business Rules. When set to `true`, it cancels the current database operation (insert, update, delete) and typically prevents the form submission. It’s crucial for server-side validation.

if ((!current.u_date1.nil()) && (!current.u_date2.nil())) { // Assuming u_date1 and u_date2 are custom date fields on the 'current' record
    var start = current.u_date1.getGlideObject().getNumericValue(); // Get numeric value (milliseconds since epoch)
    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.'); // Set an error message on the field
        current.setAbortAction(true); // Stop the database operation (e.g., insert/update)
    }
}

Result: If this script runs (e.g., in a before Business Rule) and `u_date1` is after `u_date2`, it displays an error message, highlights the `u_date1` field, and prevents the record from being saved.

Troubleshooting Common GlideRecord Hiccups

Even seasoned developers run into issues. Here are some common GlideRecord problems and how to tackle them:

  1. No Records Returned:
    • Check your query: Double-check field names and values. Are they exact? (e.g., ‘1’ vs. 1 for priority).
    • Encoded Query issues: Build your query in a list view first, then copy the encoded query to ensure correctness.
    • Data existence: Does the data you’re querying for actually exist in the instance?
  2. Infinite Loops:
    • This usually happens with `while(gr.next())` if `gr.next()` isn’t progressing or `gr.query()` was called inside the loop (don’t do that!).
    • Ensure your loop condition (`gr.next()`) is correctly advancing.
  3. Performance Problems:
    • Large Result Sets: Avoid querying entire tables without conditions. Use `addQuery()` and `setLimit()` judiciously.
    • Unindexed Fields: Querying on unindexed fields can be slow. Consider adding database indexes if truly necessary (consult with an admin).
    • Over-looping: If you’re doing complex operations inside a `while` loop over thousands of records, consider if a `updateMultiple()` or `deleteMultiple()` could achieve the same result more efficiently.
  4. Data Integrity Issues:
    • Incorrect `update()` or `deleteRecord()` targets: Ensure your `get()` or `addQuery()` is precisely identifying the record(s) you intend to modify/delete.
    • Bypassing Business Rules/Workflows: Using `setWorkflow(false)` can lead to unexpected data inconsistencies if other automations are meant to fire.
  5. Debugging:
    • `gs.print()` / `gs.info()`: Your best friends. Use them liberally to output variable values, check loop iterations, and track execution flow.
    • “Scripts – Background”: Ideal for quick testing and debugging isolated GlideRecord snippets.
    • System Logs: For Business Rules or other server-side scripts, check the System Logs for `gs.info()` and error messages.

Why GlideRecord Matters for Your Career: Interview Relevance

Understanding and mastering GlideRecord isn’t just about writing functional code; it’s a fundamental skill that underpins nearly every server-side customization in ServiceNow. For anyone aspiring to be a successful ServiceNow Developer, Administrator, or Consultant, a strong grasp of GlideRecord is non-negotiable.

What Interviewers Look For:

  • Basic CRUD Operations: Can you create, read, update, and delete records efficiently?
  • Querying Proficiency: Can you build complex queries using `addQuery()`, `addEncodedQuery()`, and various operators? This shows you can retrieve specific data effectively.
  • Performance Awareness: Do you know when to use `setLimit()`, `updateMultiple()`, or `deleteMultiple()`? Can you discuss the implications of `autoSysFields(false)` and `setWorkflow(false)`? This demonstrates maturity in your development practices.
  • Error Handling & Validation: How do you use `isValidRecord()`, `isValidField()`, and `setAbortAction()`? This speaks to your ability to write robust and resilient code.
  • Troubleshooting Skills: How would you debug a GlideRecord script that’s not returning expected results or causing performance issues?
  • ACL Understanding: Do you know how `canCreate()`, `canRead()`, etc., relate to security and user permissions?

During an interview, expect to be asked to write or explain GlideRecord snippets, troubleshoot a hypothetical scenario, or discuss best practices. Your ability to articulate your understanding and demonstrate practical application of these methods will set you apart from the crowd.

Conclusion

GlideRecord is undeniably the workhorse of server-side scripting in ServiceNow. By providing an intuitive, JavaScript-based interface to the platform’s database, it empowers developers to build powerful, customized solutions without wrestling with raw SQL. We’ve journeyed through its core concepts, explored its architecture, and dissected a comprehensive list of methods with practical examples.

Remember, the key to mastering GlideRecord, like any technical skill, is consistent practice. Experiment with these methods in your Personal Developer Instance, combine them in creative ways, and don’t be afraid to break things (in a safe environment, of course!). The more you use it, the more natural and powerful it will become in your ServiceNow development toolkit.

Happy scripting, and may your GlideRecords always return true!

Scroll to Top