ServiceNow CMDB Tutorial: A Comprehensive Guide for Beginners

Unlocking ServiceNow’s Potential: A Practical Guide to GlideRecord and the Magic of CMDB Scripting

Welcome, fellow tech enthusiasts and aspiring ServiceNow wizards! Ever found yourself staring at a blank script editor in ServiceNow, wondering how to bend the platform to your will? Or perhaps you’ve heard whispers of “Glide API” and “GlideRecord” and felt a mix of curiosity and intimidation? Well, you’re in the right place!

In the vast universe of IT Service Management (ITSM), Configuration Management Database (CMDB) sits at the core. It’s not just a fancy database; it’s the heartbeat of your IT infrastructure, housing all the critical information about your Configuration Items (CIs) – servers, applications, networks, and more. But what if you need to automate processes, pull specific data, or perform complex operations on this vital information? That’s where scripting comes in, and more specifically, ServiceNow’s powerful Glide API, with GlideRecord leading the charge.

Think of GlideRecord as your Swiss Army knife for interacting with ServiceNow’s database. It’s what empowers you to perform a wide array of actions, from creating new records to updating existing ones, all without needing to write a single line of SQL. If you’re looking to truly customize, automate, and optimize your ServiceNow instance, mastering GlideRecord is non-negotiable.

Today, we’re going on an adventure. We’ll peel back the layers of Glide API, get cozy with GlideRecord, dissect its architecture, explore its most vital methods, and then, most importantly, roll up our sleeves with practical, hands-on exercises. By the end of this guide, you’ll not only understand GlideRecord but also be able to wield its power with confidence. Ready? Let’s dive in!

Agenda: Your Roadmap to GlideRecord Mastery

Here’s a quick glance at what we’ll be covering:

  • A bird’s-eye view of Glide API.
  • Exploring the different types of Glide APIs (client-side vs. server-side).
  • What exactly is GlideRecord, and why is it your best friend?
  • Understanding the architecture behind GlideRecord.
  • How GlideRecord maps your JavaScript commands to database actions.
  • A comprehensive look at essential GlideRecord methods.
  • Hands-on GlideRecord exercises to solidify your learning.

Glide API Overview: The Language of ServiceNow Customization

At its heart, ServiceNow is a platform designed for customization. While out-of-the-box functionalities are great, the real magic happens when developers and administrators leverage the Glide API. This collection of JavaScript classes and methods allows you to interact with the platform’s core functionalities, modify default behaviors, and integrate with external systems, all through scripting.

Imagine you want to automatically set a priority on an incident based on certain conditions, or perhaps retrieve a list of all active users. Instead of wrestling with complex SQL queries, Glide API provides a more intuitive, object-oriented way to perform these database operations directly within your JavaScript code. Each API is packed with methods, and each method is designed to carry out specific tasks within your ServiceNow applications.

Types of Glide APIs: Client-Side vs. Server-Side

ServiceNow offers a rich set of APIs, broadly categorized into client-side and server-side, reflecting where their code executes:

Client-Side APIsServer-Side APIs
GlideForm: Interact with forms, set field values, hide/show fields, add messages.GlideRecord: Perform CRUD operations on database tables. (Our star for today!)
GlideUser: Access information about the current user (e.g., user roles, name).GlideSystem (gs): Provides utility functions for logging, printing, getting system properties, and more.
GlideAjax: Make asynchronous calls from client-side scripts to server-side scripts (Script Includes).GlideDate/GlideDateTime: Handle date and time manipulations.
GlideDialogWindow: Create modal dialog boxes.GlideAggregation: Perform aggregate queries (e.g., count, sum, average).
GlideList: Interact with lists.GlideElement: Interact with individual fields (elements) on a record.
GlideMenu: Interact with UI context menus.

As you can see, the distinction is crucial. Client-side APIs run in the user’s browser, affecting what the user sees and interacts with. Server-side APIs, on the other hand, run on the ServiceNow server, handling database interactions, business logic, and backend processes.

What is GlideRecord and Why You Can’t Live Without It?

Now, let’s zoom in on the main event: GlideRecord. If you’re doing any server-side scripting in ServiceNow, you’ll find yourself reaching for GlideRecord more often than not. It’s truly the most common and arguably the most important API for developers.

At its core, GlideRecord is a special JavaScript class that operates exclusively on the server side. Its primary purpose? To enable you to perform standard database operations – Create, Read, Update, and Delete (CRUD) – on any table in the ServiceNow database. The beauty is, you do this using familiar JavaScript syntax, completely abstracting away the complexities of underlying SQL queries.

Think about it: in most database environments, you’d write SQL like `SELECT * FROM incident WHERE priority = 1;`. In ServiceNow, you simply say `var inc = new GlideRecord(‘incident’); inc.addQuery(‘priority’, 1); inc.query();`. It’s a game-changer for platform development, allowing you to focus on logic rather than database syntax specifics.

This API handles both rows (records) and columns (fields) in the underlying database. So, when you create a `new GlideRecord(‘incident’)`, you’re essentially creating an object that represents a record (or a collection of records) from the `incident` table, ready for you to manipulate.

A Crucial Note of Caution: Whenever you’re performing CRUD operations, especially deletions or mass updates, it’s paramount to test your scripts thoroughly on a non-production instance (like a Dev or Test environment) first. An incorrectly constructed query or a typo in a field name can lead to unexpected results, including data loss. Always verify your queries and logic before deploying to production!

In summary, GlideRecord is your go-to for:

  1. Being the most common and versatile API for server-side scripting.
  2. Running exclusively from the server side, interacting directly with the database.
  3. Generating SQL queries dynamically behind the scenes, so you don’t have to.
  4. Effortlessly performing all CRUD (Create, Read, Update, Delete) operations.

GlideRecord Architecture: The JavaScript-to-Database Bridge

While we don’t write SQL queries directly, GlideRecord works its magic by translating your JavaScript commands into database-specific instructions. Imagine a skilled translator standing between you and the database. You speak JavaScript, and the translator (GlideRecord) converts your instructions into the language the database understands (SQL), executes them, and then translates the results back to you in JavaScript.

This abstraction simplifies development, improves security (as you’re not directly exposing SQL), and ensures consistency across the platform. So, when you write `inc.addQuery(‘active’, true);`, GlideRecord intelligently constructs the appropriate `WHERE` clause in an SQL statement for the database.

GlideRecord API Mapping: Your Commands, Database Actions

Although the original document’s diagram might have been missing, the concept is straightforward: your JavaScript code utilizing GlideRecord methods doesn’t just sit there. Each method corresponds to an action that ultimately affects data in your ServiceNow database. It’s a direct bridge: your script -> GlideRecord -> Database operation.

GlideRecord Methods: Your Toolbox for Database Interaction

The strength of GlideRecord lies in its extensive array of methods. These methods are your commands, allowing you to query, filter, create, modify, and delete records. While we won’t cover every single one of the dozens available (the list is quite exhaustive!), we’ll focus on the most commonly used and essential ones through our exercises. Think of them as the fundamental tools in your ServiceNow developer toolbox.

Where to test your scripts: For all the exercises that follow, you’ll primarily be using the “Scripts – Background” application (navigate to System Definition > Scripts - Background or simply type “Scripts – Background” in the navigator filter). This allows you to execute server-side JavaScript directly and observe the output, perfect for testing GlideRecord scripts.

GlideRecord in Action: Practical Exercises to Build Your Skills

Alright, enough theory! Let’s get our hands dirty and see GlideRecord in action. We’ll start simple and gradually build up to more complex scenarios.

1. Getting Output: Your First Steps with gs.print() and gs.info()

Before we dive into database interactions, let’s learn how to see the results of our scripts. gs.print() and gs.info() are your best friends for debugging and outputting messages.

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

Result: Both lines will print “Welcome to ServiceNow Academy” to the output area in “Scripts – Background.” gs.info() is generally preferred as it also logs to the system logs, making it more robust for debugging in various contexts.

2. Simple Arithmetic: Adding Two Numbers

Just to get comfortable with JavaScript in ServiceNow:

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

Result: 30

3. Working with query(): Retrieving All Records

This is where GlideRecord truly begins! The `query()` method executes the query you’ve defined on a table.

var inc = new GlideRecord('incident'); // Create a GlideRecord object for the 'incident' table
inc.query();                           // Execute the query (in this case, retrieve all records)
while (inc.next()) {                   // Loop through each retrieved record
    gs.print(inc.number);              // Print the 'number' field of the current incident
}

Result: This script will iterate through every single incident record in your instance and print its number (e.g., INC0000001, INC0000002, etc.).

4. Filtering with addQuery(): Display Priority 1 Incidents

To get specific records, you use `addQuery()`. This method allows you to add conditions to your query before execution.

var inc = new GlideRecord('incident');
inc.addQuery('priority', 1); // Add a condition: where 'priority' field equals '1'
inc.query();                 // Execute the filtered query
while (inc.next()) {
    gs.print(inc.number);    // Print the number of each matching incident
}

Result: You’ll see only the incident numbers that have a priority of 1.

5. Working with Multiple addQuery() Calls: Combining Conditions

You can stack multiple `addQuery()` calls. By default, these are treated as ‘AND’ conditions.

var inc = new GlideRecord('incident');
inc.addQuery('active', true);         // Condition 1: Incident is active
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: This will print incident numbers that are active AND have priority 1 AND are categorized as ‘software’.

6. The Power of addEncodedQuery(): Pre-built Filters

When you have many conditions, or want to replicate a filter from a list view, `addEncodedQuery()` is incredibly useful. It takes an encoded query string (the string ServiceNow generates when you apply filters in a list).

  • Step 1: Go to the Incident list view in ServiceNow.
  • Step 2: Apply your conditions (e.g., Active = true, Category = Software, Priority = 1).
  • Step 3: Click “Run” to apply the filter.
  • Step 4: Right-click the filter breadcrumbs (e.g., “Active = true > Category = Software…”) and select “Copy query.” This will give you the encoded query string.

Step 5: Scripting with the copied query:

var inc = new GlideRecord('incident');
// Paste your copied encoded query here
inc.addEncodedQuery('active=true^category=software^priority=1');
inc.query();
while (inc.next()) {
    gs.print(inc.number);
}

Result: Same as the previous multiple `addQuery()` example, but more concise and great for complex filters!

Exercise – 4: Using a variable for the encoded query:

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

Result: Prints records matching the conditions specified in the `ecq` variable.

7. Advanced Filtering with addQuery('String', 'Operator', 'Value')

The `addQuery()` method is flexible. You can specify an operator for more nuanced comparisons. Here are some common operators:

  • Comparison Operators: `=`, `!=`, `>`, `>=`, `<`, `<=`.
  • String Operators (must be uppercase): `IN`, `NOT IN`, `STARTSWITH`, `ENDSWITH`, `CONTAINS`, `DOES NOT CONTAIN`.

Exercise – 5: Active and Priority <= 2

var inc = new GlideRecord('incident');
inc.addQuery('active', true);        // Simpler way to say 'active=true'
inc.addQuery('priority', '<=', 2);   // Priority is less than or equal to 2
inc.query();
while (inc.next()) {
    gs.print(inc.number + ' (Priority: ' + inc.priority.getDisplayValue() + ')'); // Get display value for clarity
}

Result: This will list incident numbers that are active and have a priority of 1 (Critical) or 2 (High).

Exercise - 7: Combining `CONTAINS` with `<=2`

var inc = new GlideRecord('incident');
inc.addQuery('active', true);
inc.addQuery('priority', '<=', 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 includes "SAP".

Exercise - 8: Using the `IN` operator for multiple values

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

Result: Lists incidents where the category is either 'software' or 'hardware'. Notice `getValue()` is used to explicitly get the raw value of a field.

Exercise - 9: Working with `STARTSWITH`

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 + ' (Category: ' + inc.category.getDisplayValue() + ')');
}

Result: Prints incidents whose category starts with "net".

8. The Convenience of addActiveQuery()

A frequently used condition is `active=true`. ServiceNow provides a dedicated method for this.

var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Equivalent to inc.addQuery('active', true);
inc.addQuery('priority', 1);
inc.query();
while (inc.next()) {
    gs.info(inc.number);
}

Result: Prints active incidents with priority 1.

9. The Opposite: addInactiveQuery()

Similarly, for `active=false`:

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

Result: Prints inactive incidents (e.g., closed) that have priority 1.

10. Retrieving Your Encoded Query: getEncodedQuery()

Useful for debugging or dynamically building complex queries.

var inc = new GlideRecord('incident');
inc.addEncodedQuery('active=true^category=software^priority=1');
inc.query();
// We can call getEncodedQuery() even before the loop if we only want the query string
// Or inside the loop, it would represent the query used for that specific record
gs.print('The encoded query used was: ' + inc.getEncodedQuery());
// while (inc.next()) {
//     // ... process records ...
// }

Result: Prints the exact encoded query string that was used for the GlideRecord object.

11. Ordering Your Results: orderBy() (Ascending)

To sort your results in ascending order based on a field:

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

Result: Incidents meeting the criteria, sorted alphabetically by their short description.

12. Ordering Your Results: orderByDesc() (Descending)

To sort in descending order:

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

Result: Same incidents, but sorted in reverse alphabetical order by short description.

13. Limiting Your Results: setLimit()

For performance or specific display needs, you can limit the number of records returned.

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

Result: Prints the 10 most recently created priority 1 incidents, sorted by creation date.

14. Retrieving a Single Record: The get() Method

The `get()` method is perfect when you know the `sys_id` or a unique field (like `number`) of a specific record you want to retrieve. It attempts to load a single record into the GlideRecord object.

Exercise - 14: Get sys_id by incident number:

var inc = new GlideRecord('incident');
inc.get('number', 'INC0009005'); // Retrieve the incident with this number
gs.print('The sys_id for INC0009005 is: ' + inc.sys_id);

Result: Prints the `sys_id` associated with 'INC0009005'.

Alternatively, get incident number by sys_id:

var inc = new GlideRecord('incident');
var specificSysId = 'f892a407c611227500350438a221f736'; // Replace with a valid sys_id from your instance
if (inc.get(specificSysId)) { // If get() finds the record, it returns true
    gs.print('The incident number for ' + specificSysId + ' is: ' + inc.number);
} else {
    gs.print('Record not found for sys_id: ' + specificSysId);
}

Result: Prints the incident number corresponding to the provided `sys_id`.

15. Display Records within a Range: chooseWindow()

This method is a bit niche but can be useful for paginating results or focusing on a specific segment of a query result. It takes two arguments: the starting row (inclusive) and the ending row (exclusive).

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

Result: If there are enough records, this will print the 4th, 5th, 6th, and 7th incident numbers from your filtered and ordered list.

16. Counting Records: getRowCount()

A powerful method to quickly get the total number of records that match your query.

var inc = new GlideRecord('incident');
inc.query(); // Query all incidents
gs.print('Total incidents: ' + inc.getRowCount());

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

Exercise - 2: Counting active users:

var user = new GlideRecord('sys_user');
user.addActiveQuery(); // Only active users
user.query();
gs.print('Active users are: ' + user.getRowCount());

Result: Prints the number of active users in the `sys_user` table.

17. Identifying the Table: getTableName()

Sometimes, you might need to confirm the table name the GlideRecord object is associated with.

var cr = new GlideRecord('change_request');
gs.print('The current GlideRecord is for table: ' + cr.getTableName());

Result: Displays "change_request".

18. Getting Raw Field Values: getValue()

Retrieves the actual, raw value of a field. For reference fields, this would be the `sys_id` of the referenced record.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.query();
while (inc.next()) {
    gs.print(inc.getValue('short_description')); // Get the raw value of short_description
}

Result: Prints the short description for each active incident.

19. Getting Display Values: getDisplayValue()

For fields that have a "display value" (e.g., choice lists, reference fields), this method returns the user-friendly text, not the raw value.

var inc = new GlideRecord('incident');
inc.addQuery('priority', 1);
inc.query();
while (inc.next()) {
    gs.print('Incident: ' + inc.number + ', Priority (Display): ' + inc.priority.getDisplayValue());
}

Result: Prints the display value of the priority field (e.g., "Critical" instead of "1").

20. Checking for More Records: hasNext()

This method, often used implicitly by the `while (gr.next())` loop, explicitly checks if there are more records in the result set.

var inc = new GlideRecord('incident');
inc.query();
gs.print('Are there any incidents? ' + inc.hasNext());
// inc.next(); // If you call next() it will move to the first record
// gs.print('After next(), are there more? ' + inc.hasNext());

Result: Prints `true` if there's at least one incident record, `false` otherwise.

21. Getting the Record's Unique Identifier: getUniqueValue()

Returns the `sys_id` of the current record. This is crucial for linking records or building URLs.

var inc = new GlideRecord('incident');
inc.query();
if (inc.next()) { // Move to the first record
    var uniqueValue = inc.getUniqueValue();
    gs.print('The sys_id of the first incident is: ' + uniqueValue);
}

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

22. Setting Field Values: setValue()

This method allows you to programmatically set the value of a specific field on the current GlideRecord object. It's particularly useful when the field name itself might be dynamic.

var attriName = 'category';
var inc = new GlideRecord('incident');
inc.initialize(); // Prepare a new, empty record
inc.setValue(attriName, 'network'); // Set the category using the variable
inc.setValue('short_description', 'Critical VPN Issue');
inc.insert(); // Insert the new record into the database
gs.print('New Incident: ' + inc.number + '. Category is ' + inc.category + ' and issue is: ' + inc.short_description);

Result: Creates a new incident, sets its category to 'network' and short description, then prints the new incident number and its details.

23. Accessing a Field Object: getElement()

Returns a GlideElement object for the specified field, allowing you to interact with the field's properties and methods (like `getDisplayValue()` or `nil()`).

var elementName = 'short_description';
var inc = new GlideRecord('incident');
inc.initialize();
inc.setValue(elementName, 'I am facing VPN Problem');
inc.insert();
gs.print('The value of the ' + elementName + ' field is: ' + inc.getElement('short_description').toString());

Result: Prints the short description of the newly created incident.

24. Getting the Record's Class Name: getRecordClassName()

Retrieves the class name (which is usually the table name) of the current record.

var cr = new GlideRecord('change_request');
var grcn = cr.getRecordClassName();
gs.info('The record class name is: ' + grcn);

Result: Prints "change_request".

25. Creating New Records: initialize() and insert()

These two methods are fundamental for creating new records. `initialize()` prepares an empty GlideRecord object ready to be populated, and `insert()` saves it to the database.

var inc = new GlideRecord('incident');
inc.initialize();                   // Clears the current record and prepares for a new one
inc.category = 'network';           // Set field values directly
inc.short_description = 'Firewall Issue needs attention';
inc.priority = 1;
inc.insert();                       // Saves the new record to the incident table
gs.print('Created new incident: ' + inc.number); // Prints the number of the newly created incident

Result: A new incident record is created with the specified details, and its incident number is printed.

26. Checking if a Record is New: isNewRecord() and newRecord()

`isNewRecord()` tells you if the current GlideRecord object has been saved to the database yet. `newRecord()` creates a new record and assigns a temporary `sys_id` but doesn't save it until `insert()` is called.

var inc = new GlideRecord('incident');
inc.newRecord(); // Similar to initialize, but also assigns a new sys_id
gs.info('Is this a new record before inserting? ' + inc.isNewRecord());
inc.short_description = 'Testing newRecord method';
inc.insert();
gs.info('Is this a new record after inserting? ' + inc.isNewRecord());

Result: First print will be `true`, second print will be `false`.

27. Validating a GlideRecord: isValid()

Checks if the GlideRecord object is associated with a valid, existing table.

var inc = new GlideRecord('incident');
gs.print('Is "incident" a valid table? ' + inc.isValid());
var invalidGr = new GlideRecord('non_existent_table');
gs.print('Is "non_existent_table" a valid table? ' + invalidGr.isValid());

Result: First print: `true`. Second print: `false`.

28. Validating a Field: isValidField()

Checks if a specified field exists in the table associated with the GlideRecord object.

var inc = new GlideRecord('incident');
gs.print('Does the "incident" table have a "category" field? ' + inc.isValidField('category'));
gs.print('Does the "incident" table have a "my_custom_field" field? ' + inc.isValidField('my_custom_field'));

Result: First print: `true`. Second print: `false` (assuming 'my_custom_field' doesn't exist).

29. Getting a Record's Link: getLink() and `gs.getProperty()`

You can generate a direct URL to a ServiceNow record, which is very useful for notifications or integrations.

var inc = new GlideRecord('incident');
inc.addActiveQuery();
inc.addQuery('priority', 1);
inc.setLimit(1); // Just get one for the example
inc.query();
if (inc.next()) {
    // gs.getProperty('glide.servlet.uri') gives you the base URL of your instance
    var recordLink = gs.getProperty('glide.servlet.uri') + inc.getLink(false); // false for relative URL
    gs.print('Link to incident ' + inc.number + ': ' + recordLink);
}

Result: Returns a clickable URL to the first active, priority 1 incident found.

30. Verifying Record Retrieval: isValidRecord()

After a `get()` or `query()`, this method tells you if an actual record was successfully loaded into the GlideRecord object.

var inc = new GlideRecord('incident');
inc.get('number', 'INC0010012'); // Replace with a valid incident number in your instance
gs.print('Incident ' + inc.number + ' exists: ' + inc.isValidRecord());

var nonExistentInc = new GlideRecord('incident');
nonExistentInc.get('number', 'INC9999999'); // An incident that likely doesn't exist
gs.print('Incident ' + nonExistentInc.number + ' exists: ' + nonExistentInc.isValidRecord());

Result: For an existing record, it will print `true`. For a non-existent record, it will print `false`.

31. Explicitly Creating a New Record: newRecord()

A variation of `initialize()`, `newRecord()` is often used when you specifically want to create a new, blank record and set its default values before populating it.

var inc = new GlideRecord('incident');
inc.newRecord(); // Creates a new GlideRecord instance for a new record
inc.short_description = 'Creating new record with newRecord() method';
inc.category = 'software';
inc.insert();
gs.print('New record created: ' + inc.number);

Result: A new incident is created and its number is printed.

32. Querying for Null Values: addNullQuery()

Finds records where a specific field's value is empty or 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 + ' has a null short description');
}

Result: Lists incident numbers where the short description field is empty.

33. Querying for Not-Null Values: addNotNullQuery()

The opposite of `addNullQuery()`, this finds records where a field has any value.

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

Result: Lists incident numbers where the short description field is not empty.

34. Updating a Single Record: update()

To modify an existing record, you first retrieve it, change its field values, and then call `update()`.

var inc = new GlideRecord('incident');
if (inc.get('number', 'INC0000057')) { // Replace with an actual incident number you want to update
    inc.setValue('state', 2); // Set state to 'Active' (assuming 2 is 'Active' in your instance)
    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: Updates the state of the specified incident. Remember to use valid incident numbers from your instance!

35. Updating Multiple Records: updateMultiple()

This powerful (and potentially dangerous!) method updates all records that match the current query with the specified changes, without needing to loop through each one.

var inc = new GlideRecord('incident');
inc.addQuery('category', 'hardware');  // Query for all 'hardware' incidents
inc.setValue('category', 'software');  // Change their category to 'software'
inc.updateMultiple();                  // Execute the mass update
gs.print('All hardware incidents changed to software category. Please verify.');

Result: All incidents previously categorized as 'hardware' will now be 'software'. Use with extreme caution on non-production instances!

36. Deleting a Single Record: deleteRecord()

Deletes the current record loaded into the GlideRecord object.

var inc = new GlideRecord('incident');
if (inc.get('number', 'INC0010013')) { // Replace with an incident number you want to delete
    inc.deleteRecord();                // Delete the record
    gs.print('Incident ' + inc.number + ' deleted successfully.');
} else {
    gs.print('Incident INC0010013 not found for deletion.');
}

Result: Deletes the specified incident record. Again, exercise extreme caution!

37. Deleting Multiple Records: deleteMultiple()

Deletes all records that match the current query condition without looping.

var inc = new GlideRecord('incident');
inc.addQuery('priority', 4); // Query for all priority 4 incidents (e.g., 'Low')
inc.deleteMultiple();       // Delete all matching records
gs.print('All priority 4 incidents deleted. Please verify.');

Result: All incidents with priority 4 will be permanently deleted. This is highly destructive; use only in test environments.

38. Checking Permissions: canCreate()

Determines if the currently logged-in user has permission (based on ACLs) to create new records in the specified table.

var inc = new GlideRecord('incident');
gs.print('Can I create incidents? ' + inc.canCreate());

Result: `true` or `false` depending on your user's roles and ACLs for the incident table.

39. Checking Permissions: canRead()

Checks if the user can read records from the table.

var inc = new GlideRecord('incident');
gs.print('Can I read incidents? ' + inc.canRead());

Result: `true` or `false`.

40. Checking Permissions: canWrite()

Checks if the user can modify records in the table.

var inc = new GlideRecord('incident');
gs.print('Can I write to incidents? ' + inc.canWrite());

Result: `true` or `false`.

41. Checking Permissions: canDelete()

Checks if the user can delete records from the table.

var inc = new GlideRecord('incident');
gs.print('Can I delete incidents? ' + inc.canDelete());

Result: `true` or `false`.

42. Bypassing System Fields and Business Rules: autoSysFields() and setWorkflow()

These are advanced methods for very specific scenarios, typically when you need to perform an update without triggering all the usual platform machinery.

  • autoSysFields(false): Prevents the automatic update of system fields like `sys_updated_by`, `sys_updated_on`, `sys_mod_count`. Useful for data imports or migrations where you want to preserve historical stamps. Note: This method might not work as expected in scoped applications due to platform changes.
  • setWorkflow(false): Prevents any business rules (including async ones), notifications, or flow triggers from running for the specific `update()` operation. Use with extreme care, as it can bypass critical business logic.

Exercise - 43: Updating records without updating system fields or triggering workflows:

var inc = new GlideRecord('incident');
inc.addQuery('state', 1); // Incidents in 'New' state (assuming '1' is 'New')
inc.query();
while (inc.next()) {
    inc.autoSysFields(false); // Do not update 'updated_by', 'updated_on', 'mod_count'
    inc.setWorkflow(false);   // Do not run business rules, notifications, etc.
    inc.setValue('state', 2); // Change state to 'Active'
    inc.update();
    gs.print('Updated incident ' + inc.number + ' to state 2 without system field updates or workflows.');
}

Result: Incidents are updated, but their audit trail (updated by/on) and associated workflows are skipped. Verify carefully!

43. Performing a Join Query: addJoinQuery()

This allows you to query records from one table based on conditions in a related table, similar to an SQL JOIN. It's an inner join by default.

var prob = new GlideRecord('problem');
// Find problems where the 'opened_by' field matches the 'caller_id' field of an associated incident.
// The first argument is the joining table, the second is the current table's field, the third is the joining table's field.
prob.addJoinQuery('incident', 'opened_by', 'caller_id');
prob.query();
while (prob.next()) {
    gs.print(prob.number + ' (Opened by: ' + prob.opened_by.name + ')');
}

Result: Prints problem numbers that have at least one associated incident where the problem's 'opened_by' matches the incident's 'caller_id'.

44. Date Comparison and Aborting Actions: getGlideObject(), getNumericValue(), and setAbortAction()

This is a more advanced example, often seen in Business Rules, for complex validation, especially with dates.

  • `getGlideObject()`: Converts a GlideElement (a field object) into a GlideObject (its underlying data type, e.g., GlideDateTime).
  • `getNumericValue()`: Returns the numeric value of a GlideDateTime object, often used for direct comparison (e.g., milliseconds since epoch).
  • `setAbortAction(true)`: Typically used in Business Rules to stop the current database operation (insert, update, delete) from completing if a validation fails.
if (!current.u_date1.nil() && !current.u_date2.nil()) { // Check if both custom date fields are not empty
    var start = current.u_date1.getGlideObject().getNumericValue(); // Get numeric value of start date
    var end = current.u_date2.getGlideObject().getNumericValue();   // Get numeric value of end date
    if (start > end) { // If start date is after end date
        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 current save/update operation
    }
}

Result: If this script runs in a Business Rule on a record with `u_date1` and `u_date2` fields, and `u_date1` is later than `u_date2`, it will display an error message, highlight `u_date1` with an error, and prevent the record from being saved.

Troubleshooting Common GlideRecord Issues

Even the best developers run into issues. Here are some common GlideRecord pitfalls and how to debug them:

  • Typos and Incorrect Field Names: This is probably the most frequent error. If `inc.addQuery('priorityy', 1)` is used instead of `inc.addQuery('priority', 1)`, your query won't work. Check field names carefully, and use `gr.isValidField('your_field_name')` to verify.
  • No Records Returned: If your `while (gr.next())` loop never runs, it means your query found no records.
    • Double-check your `addQuery()` conditions. Are they too restrictive?
    • Verify the data in the table manually through a list view. Does data matching your conditions actually exist?
    • Use `gs.print(gr.getEncodedQuery())` before `gr.query()` to see the exact query string being executed. Copy it and paste it into a list view filter to visually confirm the results.
  • Data Loss/Unexpected Updates: This usually happens with `updateMultiple()` or `deleteMultiple()` when the query criteria are too broad. ALWAYS test these on a non-production instance. Use `gs.print(gr.getRowCount())` before `updateMultiple()` or `deleteMultiple()` to see how many records will be affected.
  • ACL Issues: If your script runs fine as an administrator but fails for a different user, it's likely an Access Control List (ACL) problem. Use `canCreate()`, `canRead()`, `canWrite()`, `canDelete()` to debug permissions programmatically.
  • Performance Problems: Queries on very large tables without proper filtering or limiting can impact performance.
    • Always use `addQuery()` to narrow down your result set.
    • Employ `setLimit()` if you only need a few records.
    • Avoid complex `while (gr.next())` loops on hundreds of thousands of records. Consider GlideAggregate for counts/sums or background jobs for large data processing.
  • Client-Side vs. Server-Side Confusion: Remember, GlideRecord is SERVER-SIDE. You cannot call it directly from a Client Script or UI Policy. For client-side interaction with server data, you'd typically use `GlideAjax`.

GlideRecord in the Interview Room: What to Expect

Mastering GlideRecord isn't just for coding; it's a hot topic in ServiceNow interviews. Be prepared to discuss:

  • What is GlideRecord? (Its purpose, server-side nature, CRUD operations).
  • Difference between `gs.print()` and `gs.info()`? (Output vs. logging).
  • Explain CRUD operations using GlideRecord methods. (`initialize()`, `insert()`, `addQuery()`, `query()`, `next()`, `update()`, `deleteRecord()`, `deleteMultiple()`).
  • When would you use `addQuery()` versus `addEncodedQuery()`? (Simplicity vs. complexity/reusability from list views).
  • What's the purpose of `setWorkflow(false)` or `autoSysFields(false)`? (Bypassing business rules/system field updates for specific use cases).
  • How do you debug a GlideRecord script? (`gs.print`, `gs.info`, checking encoded queries, verifying data).
  • What are the performance considerations when using GlideRecord? (Filtering, limiting, avoiding large loops).
  • Can you call GlideRecord from a Client Script? (No, explain `GlideAjax` as the alternative).
  • What are the security implications of using GlideRecord? (ACLs, data loss prevention, testing in non-prod).

Wrapping Up Your GlideRecord Journey

Phew! That was quite a journey, wasn't it? You've just traversed the core landscape of ServiceNow scripting, delving deep into the indispensable world of Glide API and, more specifically, the mighty GlideRecord. From understanding its fundamental purpose as a database abstraction layer to executing complex queries and performing CRUD operations, you now have a solid foundation.

Remember, theory is great, but practice makes perfect. Keep experimenting with these methods in your personal developer instance. Try different combinations, explore new tables, and build small scripts to solve hypothetical problems. The more you code, the more intuitive GlideRecord will become.

GlideRecord isn't just a tool; it's the superpower that lets you truly automate, customize, and extend the ServiceNow platform, making it sing to your organization's unique rhythm. Go forth and script, fellow ServiceNow champions!

Scroll to Top