Unlocking the Database: A Developer’s Guide to GlideRecord CRUD Operations in ServiceNow
Hey there, fellow ServiceNow enthusiast! Ever found yourself needing to dig into your instance’s data, manipulate records, or build robust server-side scripts that truly make your applications sing? If so, you’re in the right place. Today, we’re going to pull back the curtain on one of ServiceNow’s most powerful and frequently used server-side APIs: GlideRecord.
Think of GlideRecord as your direct line to the database – without having to write a single line of SQL. It’s the engine behind so much of what makes ServiceNow flexible and customizable. Whether you’re a seasoned developer or just starting your journey, mastering GlideRecord’s Create, Read, Update, and Delete (CRUD) operations is absolutely essential.
We’ll cover everything from the basics of what GlideRecord is, how it fits into the broader ServiceNow API landscape, its architecture, and then dive deep into practical examples for each CRUD operation. So, grab your favorite beverage, open up a personal developer instance, and let’s get coding!
Agenda
- Glide API Overview
- Types of Glide API’s
- What is GlideRecord and Usage
- GlideRecord Architecture
- API Mapping: ServiceNow to Database
- GlideRecord Methods: The Power Tools
- GlideRecord Exercises: Hands-On Practice
Glide API Overview: Your Swiss Army Knife for Customization
At its core, ServiceNow is built to be extended and customized. The magic behind this flexibility largely comes from a collection of JavaScript classes and functions known collectively as the Glide APIs. These APIs empower developers like us to go beyond out-of-the-box functionality, integrate with other systems, automate complex workflows, and generally tailor the platform to specific business needs.
Instead of writing raw SQL queries to interact with the underlying database (which, frankly, could be a security nightmare and lead to platform instability), Glide APIs provide a safe, structured, and developer-friendly way to perform database operations, manipulate forms, interact with the user, and much more. Each API is a specialized toolkit, packed with methods designed for specific tasks within the ServiceNow ecosystem.
When you hear “Glide API,” think of it as a set of standardized instructions that allow your scripts to “talk” to ServiceNow’s various components. It’s how you turn your creative ideas into functional code on the platform.
Types of Glide APIs: Client-Side vs. Server-Side
The Glide APIs are broadly categorized based on where they execute:
Client-Side APIs
These APIs run in the user’s web browser. They’re all about enhancing the user experience, validating input on forms, and dynamically altering what the user sees without needing to send requests back to the server for every little interaction.
- GlideForm (g_form): Manipulate forms (e.g., hide/show fields, set values, add messages).
- GlideUser (g_user): Get information about the currently logged-in user.
- GlideAjax: Asynchronously fetch data from the server without refreshing the page.
- GlideDialogWindow: Create pop-up dialog windows.
- GlideList, GlideMenu, GlideElement: Interact with lists, menus, and individual form elements.
Server-Side APIs
These APIs run on the ServiceNow application server. They’re the workhorses for database interactions, complex business logic, integrations, and anything that needs access to the full power of the platform’s backend. This is where GlideRecord lives!
- GlideRecord: Our star of the show! Performs CRUD operations on database tables.
- GlideSystem (gs): A utility API for logging, session information, properties, and more.
- GlideDate / GlideDateTime: Work with date and time values.
- GlideAggregation: Perform aggregate queries on large datasets.
- And many more for specific use cases like email, XML, JSON, etc.
What is GlideRecord and Usage?
Alright, let’s zoom in on the main event: GlideRecord. If you spend any significant time scripting in ServiceNow, GlideRecord will become your best friend. It’s arguably the most common and vital server-side API you’ll encounter.
At its core, GlideRecord is a special JavaScript class (though it’s fundamentally a wrapper around Java classes under the hood) that operates exclusively on the server side. Its primary purpose is to allow you to interact with the ServiceNow database – to perform those essential CRUD operations – without ever having to write direct SQL queries.
Think of it as a super-smart translator. You, the developer, write JavaScript using GlideRecord methods, and GlideRecord translates those commands into optimized SQL queries that the database understands. This abstraction is a huge win for security, performance, and developer sanity. It handles both rows (records) and columns (fields) in the underlying database tables, giving you complete programmatic control over your data.
Why can’t we just write SQL? Good question! Direct SQL interaction is generally restricted in ServiceNow to maintain platform integrity, ensure security, and standardize how data is accessed and manipulated. GlideRecord enforces best practices and integrates seamlessly with ServiceNow’s robust Access Control Lists (ACLs), ensuring that your scripts only perform actions they are permitted to.
A CRUCIAL NOTE for Developers and Interviewers: Always, always, always test your GlideRecord scripts on a non-production instance (like a development or sandbox environment) before even thinking about deploying them to production! An incorrectly constructed query, a typo in a field name, or a logical error in your script can lead to invalid queries. Running an insert(), update(), or especially a deleteRecord() or deleteMultiple() method on a bad query can result in unintended data manipulation or, even worse, significant data loss. This is a common interview question: “What precautions do you take when writing server-side scripts involving database operations?” Your answer should always highlight thorough testing in non-production environments.
Key Characteristics of GlideRecord:
- Most Common API: You’ll see it everywhere in Business Rules, Script Includes, Fix Scripts, UI Actions, and more.
- Running from Server Side: Executes on the ServiceNow application server, not in the user’s browser.
- Generates SQL Queries: Translates your JavaScript commands into database-friendly SQL.
- Performs CRUD Operations: The primary tool for Creating, Reading, Updating, and Deleting records.
GlideRecord Architecture: The Bridge to Your Data
So, how does GlideRecord fit into the grand scheme of things? Imagine your ServiceNow instance as a bustling city, and the database as its central vault, holding all the valuable information. GlideRecord acts as the secure, authorized messenger service that transports requests and data between your scripts and that vault.
When you write a server-side script – perhaps a Business Rule that triggers after an Incident is updated, a UI Action button that closes a Change Request, or a Fix Script to clean up old data – that script isn’t directly talking to the database. Instead, it interacts with GlideRecord using JavaScript commands.
Here’s the simplified flow:
- Your Script (e.g., Client Script, UI Action, Business Rule, Script Include): You initiate a GlideRecord object for a specific table.
- GlideRecord: This object then takes your JavaScript instructions (like `addQuery`, `insert`, `update`) and dynamically constructs the appropriate SQL query.
- Database: The generated SQL query is executed against the underlying database.
- Results: The database returns the requested data or confirms the operation, which GlideRecord then translates back into a JavaScript-friendly format for your script to process.
(Imagine a diagram here: Your Script (Client Script, UI Action, Business Rule) → initiates GlideRecord Object → GlideRecord processes JavaScript methods → Generates SQL Query → Database executes Query → Results sent back to GlideRecord → GlideRecord returns results to your Script.)
GlideRecord API Mapping: JavaScript to SQL (Behind the Scenes)
This mapping is where GlideRecord truly shines. You think in terms of ServiceNow tables and fields, and GlideRecord translates that into database tables and columns. This means you don’t need to know the exact SQL syntax for joining tables, filtering records, or updating values. GlideRecord handles it all, providing a consistent and intuitive JavaScript interface.
(Imagine a diagram here: ServiceNow Platform (JavaScript) → GlideRecord → Database (SQL))
For instance, when you write `inc.addQuery(‘active’, true);`, GlideRecord internally translates that into a SQL `WHERE active = 1` clause. This abstraction allows developers to focus on the business logic rather than database intricacies, while also benefiting from ServiceNow’s built-in query optimizations and security checks.
GlideRecord Methods: Your Power Tools for Data Manipulation
GlideRecord comes packed with a vast array of methods, each designed to perform a specific action or retrieve particular information. While we won’t cover every single one (there are many!), we’ll focus on the most commonly used methods, especially those critical for CRUD operations.
When approaching GlideRecord, it’s helpful to categorize methods by their primary function:
Core CRUD Operations
query(): Executes the constructed query.insert(): Creates a new record.update(): Updates an existing record.deleteRecord(): Deletes a single record.deleteMultiple(): Deletes multiple records based on a query.
Query Building and Filtering
addQuery(): Adds a simple filter to the query.addEncodedQuery(): Adds a complex, pre-defined filter string.addActiveQuery(): Adds a filter for active records (`active=true`).addInactiveQuery(): Adds a filter for inactive records (`active=false`).addNullQuery(): Filters records where a field’s value is null.addNotNullQuery(): Filters records where a field’s value is not null.addJoinQuery(): Adds a join to another table for complex queries.
Navigation and Iteration
next(): Moves to the next record in the query result set (used in `while` loops).hasNext(): Checks if there are more records in the result set.
Record Retrieval and Information
get(): Retrieves a single record by its `sys_id` or another unique field.getRowNumber(): Gets the current row number.getRowCount(): Gets the total number of records in the result set.getValue(): Retrieves the actual value of a field.getDisplayValue(): Retrieves the display value (what the user sees) of a field.getEncodedQuery(): Returns the encoded query string that was used.getTableName(): Returns the name of the table the GlideRecord is querying.getUniqueValue(): Gets the unique key (usually `sys_id`) of the current record.getElement(): Retrieves a specific field object for the current record.getLink(): Retrieves a link to the current record.getRecordClassName(): Retrieves the table name of the current record.
Record Manipulation and Utility
initialize(): Prepares a new, empty record for insertion.newRecord(): Creates a new record with default values.setValue(): Sets the value of a specific field.setDisplayValue(): Sets the display value of a field (useful for reference fields).setLimit(): Limits the number of records returned by the query.orderBy(): Sorts the results in ascending order.orderByDesc(): Sorts the results in descending order.chooseWindow(): Limits the records returned to a specific range (offset and count).autoSysFields(): Controls whether system fields (e.g., `sys_updated_on`) are updated.setWorkflow(): Controls whether Business Rules (workflow) are run.canCreate(), canRead(), canWrite(), canDelete(): Checks ACL permissions for the current user.isValid(): Checks if the GlideRecord object is valid (i.e., the table exists).isValidField(): Checks if a specified field exists in the current table.isValidRecord(): Checks if a record was actually returned by a query/get operation.isNewRecord(): Checks if the current record has not yet been inserted into the database.setAbortAction(): Stops the current database operation (e.g., insert/update).
Note: All the exercises and examples we’ll explore below are best executed in the “Scripts – Background” module within your ServiceNow instance. This provides a sandbox environment to test your server-side scripts quickly. You can find it by typing “Scripts – Background” into the Application Navigator.
GlideRecord Exercises: Hands-On Practice to Master CRUD
Now for the fun part: getting our hands dirty with some code! We’ll walk through practical examples for each method, explaining the “why” behind the “what.”
Getting Output in ServiceNow (gs.print vs. gs.info)
Before we dive into GlideRecord, let’s understand how to see the results of our scripts.
gs.print() and gs.info() are both ways to output messages to the System Log.
gs.print(): Outputs a message at the “Info” level. It’s an older method but still widely used.gs.info(): Also outputs a message at the “Info” level, but is generally preferred in modern development as it’s part of the more comprehensivegs(GlideSystem) API. Other levels likegs.debug(),gs.warn(),gs.error()are also available.
gs.print('Welcome to ServiceNow Academy - using print');
gs.info('Welcome to ServiceNow Academy - using info');Result: Both lines will appear in your “Scripts – Background” output window and in the System Logs with an ‘Info’ level.
Basic JavaScript in ServiceNow
Just to ensure we’re on the same page, here’s a simple JavaScript operation.
var a = 10;
var b = 20;
var c = a + b;
gs.print(a + b); // Or gs.print(c);Result: 30
Reading Records: The ‘R’ in CRUD
1. Basic Querying with query() and next()
This is your bread and butter for fetching records. You instantiate a GlideRecord object for a table, call `query()`, and then loop through the results using `while(inc.next())`.
var inc = new GlideRecord('incident'); // 'incident' is the table name
inc.query(); // Executes the query to fetch all records
while (inc.next()) { // Loops through each record found
gs.print(inc.number + ' - ' + inc.short_description); // Prints the incident number and short description
}Result: This will print the number and short description of every single incident record in your instance. (You’ll likely see a lot of them!)
2. Filtering with addQuery()
`addQuery()` allows you to add specific conditions to your query, narrowing down the results.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1'); // Add a query condition: priority equals 1 (Critical)
inc.query();
while (inc.next()) {
gs.print('Critical Incident: ' + inc.number);
}Result: Prints the numbers of all incident tickets where the priority is set to ‘1’ (Critical).
3. Multiple Filters with addQuery()
You can chain multiple `addQuery()` calls. By default, these are treated as “AND” conditions.
var inc = new GlideRecord('incident');
inc.addQuery('active', true); // Query 1: only active incidents
inc.addQuery('priority', '1'); // Query 2: with priority 1
inc.addQuery('category', 'software'); // Query 3: in the 'software' category
inc.query();
while (inc.next()) {
gs.print('Filtered Incident: ' + inc.number + ' - ' + inc.short_description);
}Result: Prints incident numbers and short descriptions for all active, priority 1, software category incidents.
4. Using addEncodedQuery() for Complex Filters
When you have many conditions or complex “OR” statements, `addEncodedQuery()` is incredibly useful. The best part? You can build these queries directly from any list view in ServiceNow!
- Step 1: Navigate to the Incident list view (`incident.do`).
- Step 2: Apply your desired conditions (e.g., active = true AND priority = 1 AND category = software).
- Step 3: Click “Run.”
- Step 4: Right-click on the breadcrumbs (or the filter icon), and select “Copy query.”
- Step 5: Paste this entire query string into your script.
var inc = new GlideRecord('incident');
var encodedQuery = 'active=true^category=software^priority=1'; // Encoded query copied from a list
inc.addEncodedQuery(encodedQuery);
inc.query();
while (inc.next()) {
gs.print('Encoded Query Result: ' + inc.number + ' - ' + inc.short_description);
}Result: Same as the multiple `addQuery()` example, but with a more concise way to define the filter.
Interview Tip: Being able to leverage `addEncodedQuery()` demonstrates a deeper understanding of ServiceNow development, especially for complex filtering scenarios.
5. Advanced Operators with addQuery()
`addQuery()` supports various operators, not just equality. These are often mapped directly from SQL operators.
Numerical/Date Operators: `=`, `!=`, `>`, `>=`, `<`, `<=`
String Operators (case-sensitive, use uppercase): `=`, `!=`, `IN`, `NOT IN`, `STARTSWITH`, `ENDSWITH`, `CONTAINS`, `DOES NOT CONTAIN`, `INSTANCEOF`
Example: Priority less than or equal to 2 (Critical or High)
var inc = new GlideRecord('incident');
inc.addQuery('active', true); // Only active incidents
inc.addQuery('priority', '<=', 2); // Priority is 1 or 2
inc.query();
while (inc.next()) {
gs.print('High Priority Active Incident: ' + inc.number + ' - Priority: ' + inc.priority.getDisplayValue());
}Result: Prints active incidents with Priority 1 (Critical) or Priority 2 (High).
Example: Using `CONTAINS`
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 contains "SAP."
Example: Using `IN` operator
This is great for fetching records that match one of several values for a single field.
var categories = ['software', 'hardware', 'network'];
var inc = new GlideRecord('incident');
inc.addQuery('category', 'IN', categories); // Category is one of 'software', 'hardware', or 'network'
inc.query();
while (inc.next()) {
gs.print(inc.getValue('number') + ' - ' + inc.getValue('category') + ' - ' + inc.getValue('short_description'));
}Result: Prints incidents belonging to the 'software', 'hardware', or 'network' categories.
6. Convenience Methods: addActiveQuery() and addInactiveQuery()
These are just shorthand for `addQuery('active', true)` and `addQuery('active', false)`.
Example: Active incidents with priority 1
var inc = new GlideRecord('incident');
inc.addActiveQuery(); // Equivalent to inc.addQuery('active', true);
inc.addQuery('priority', '1');
inc.query();
while (inc.next()) {
gs.info('Active P1 Incident: ' + inc.number);
}Result: Prints active incidents with priority 1.
Example: Inactive incidents with priority 1
var inc = new GlideRecord('incident');
inc.addInactiveQuery(); // Equivalent to inc.addQuery('active', false);
inc.addQuery('priority', '1');
inc.query();
while (inc.next()) {
gs.print('Inactive P1 Incident: ' + inc.number);
}Result: Prints inactive incidents with priority 1 (often closed incidents).
7. Using getEncodedQuery()
Useful for debugging or dynamically logging the query your script is constructing.
var inc = new GlideRecord('incident');
inc.addEncodedQuery('active=true^category=software^priority=1');
inc.query();
if (inc.next()) { // Just checking the first record to get the query
gs.print('The encoded query used was: ' + inc.getEncodedQuery());
}Result: Prints the encoded query string: `active=true^category=software^priority=1`.
8. Ordering Results with orderBy() and orderByDesc()
Sort your query results for better readability or specific use cases.
Example: Order by short description (ascending)
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.orderBy('short_description'); // Sorts by short_description in ascending order
inc.setLimit(5); // Just for a manageable output
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - ' + inc.short_description);
}Result: Prints 5 priority 1 incidents, sorted alphabetically by short description.
Example: Order by short description (descending)
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.orderByDesc('short_description'); // Sorts by short_description in descending order
inc.setLimit(5);
inc.query();
while (inc.next()) {
gs.print(inc.number + ' - ' + inc.short_description);
}Result: Prints 5 priority 1 incidents, sorted reverse-alphabetically by short description.
9. Limiting Results with setLimit()
Only retrieve a specific number of records. Essential for performance on large tables.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.orderByDesc('sys_created_on'); // Get the latest ones
inc.setLimit(10); // Only retrieve the top 10
inc.query();
while (inc.next()) {
gs.print('Latest P1 Incident: ' + inc.number + ' - Created On: ' + inc.sys_created_on);
}Result: Prints the 10 most recently created priority 1 incidents.
10. Retrieving a Single Record with get()
This method is perfect when you know the `sys_id` or another unique field value of the record you want. It's more efficient than a `query()` followed by `next()` if you're only interested in one specific record.
Example: Get sys_id by incident number
var inc = new GlideRecord('incident');
// Note: 'number' is a unique field for incidents. For sys_id, just inc.get(sys_id_string);
if (inc.get('number', 'INC0009005')) { // If record is found, 'get' returns true
gs.print('Sys_id for INC0009005: ' + inc.sys_id);
} else {
gs.print('Incident INC0009005 not found.');
}Result: Prints the `sys_id` associated with 'INC0009005'.
Example: Get incident number by sys_id
var inc = new GlideRecord('incident');
var specificSysId = 'YOUR_INCIDENT_SYS_ID_HERE'; // Replace with an actual sys_id from your instance
if (inc.get(specificSysId)) {
gs.print('Incident number for ' + specificSysId + ': ' + inc.number);
} else {
gs.print('Incident with sys_id ' + specificSysId + ' not found.');
}Result: Prints the incident number for the given `sys_id`.
11. Using chooseWindow()
This method allows you to fetch a specific "window" of records, specifying a starting offset and a count. The first value is inclusive, the second is exclusive.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1');
inc.addActiveQuery();
inc.orderBy('number'); // Order for consistent windowing
inc.chooseWindow(3, 7); // Start at index 3 (4th record), fetch up to index 7 (but not including)
// This will fetch records at index 3, 4, 5, 6 (a total of 4 records)
inc.query();
while (inc.next()) {
gs.print('Windowed Incident: ' + inc.number);
}Result: Prints records corresponding to the 4th, 5th, 6th, and 7th active priority 1 incidents (assuming 0-based indexing for the start). For `chooseWindow(start, end)`, it includes `start` but excludes `end`.
12. Counting Records with getRowCount()
Need to know how many records matched your query? `getRowCount()` is your friend.
var inc = new GlideRecord('incident');
inc.query();
gs.print('Total number of incidents: ' + inc.getRowCount());Result: Prints the total number of records in the Incident table.
Example: Count 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 in the `sys_user` table.
13. Getting Field Values: getValue() vs. getDisplayValue()
These methods are crucial for working with field data, especially reference or choice fields.
getValue('fieldName'): Returns the actual stored value (e.g., `sys_id` for a reference field, integer for a choice field).getDisplayValue('fieldName'): Returns the user-friendly, displayed value (e.g., user's name for a reference field, string for a choice field).
Example: Get actual and display value for priority
var inc = new GlideRecord('incident');
inc.addQuery('priority', '1'); // Get a critical incident
inc.setLimit(1);
inc.query();
if (inc.next()) {
gs.print('Priority (Actual Value): ' + inc.getValue('priority')); // e.g., '1'
gs.print('Priority (Display Value): ' + inc.getDisplayValue('priority')); // e.g., '1 - Critical'
gs.print('Category (Actual Value): ' + inc.category); // You can also directly access fields
gs.print('Category (Display Value): ' + inc.getDisplayValue('category'));
}Result: Shows the difference between the stored value and the displayed label for fields.
14. Other Useful Read-Related Methods
inc.getTableName(): Returns `incident` for our example.inc.hasNext(): Returns `true` if there are more records to iterate, `false` otherwise (useful outside `while` loops for a quick check).inc.getUniqueValue(): Gets the `sys_id` of the current record.inc.getElement('short_description'): Returns a GlideElement object for the 'short_description' field.inc.getRecordClassName(): Returns the table name (same as `getTableName()`).inc.getLink(false): Gets the relative URL to the current record. Combine with `gs.getProperty('glide.servlet.uri')` for a full URL.
var inc = new GlideRecord('incident');
inc.setLimit(1);
inc.query();
if (inc.next()) {
gs.print('Table Name: ' + inc.getTableName());
gs.print('Has Next? ' + inc.hasNext());
gs.print('Unique Value (sys_id): ' + inc.getUniqueValue());
var element = inc.getElement('short_description');
if (element) {
gs.print('Short Description (from Element): ' + element.toString());
}
gs.print('Record Class Name: ' + inc.getRecordClassName());
gs.print('Full Record Link: ' + gs.getProperty('glide.servlet.uri') + inc.getLink(false));
}Result: Displays various properties of a single incident record.
Creating Records: The 'C' in CRUD
15. Using initialize() and insert()
To create a new record, you first "initialize" a new empty record, set its field values, and then "insert" it into the database.
var newIncident = new GlideRecord('incident');
newIncident.initialize(); // Prepares a new, empty incident record
newIncident.category = 'network'; // Set field values directly
newIncident.short_description = 'Urgent Firewall Issue - Automated creation';
newIncident.priority = '1';
newIncident.insert(); // Saves the new record to the database
gs.print('New Incident Created: ' + newIncident.number + ' with Sys_ID: ' + newIncident.sys_id);Result: A brand new incident record will be created in your instance, and its number and `sys_id` will be printed.
16. Using newRecord() (and isNewRecord())
`newRecord()` also creates a new record but also sets default values and assigns a `sys_id` immediately. `isNewRecord()` checks if the record exists in the database yet.
var inc = new GlideRecord('incident');
inc.newRecord(); // Creates a new record with default values and a sys_id
gs.info('Is this a new record? ' + inc.isNewRecord()); // Should return true
inc.short_description = 'Testing newRecord() method';
inc.insert();
gs.info('New Incident created with newRecord(): ' + inc.number);
gs.info('Is this a new record after insert? ' + inc.isNewRecord()); // Should return false nowResult: Prints `true` before `insert()` and `false` after. A new incident is created.
17. Setting Values with setValue()
An alternative to direct field access (`inc.category = 'network'`). Useful when field names are dynamic.
var inc = new GlideRecord('incident');
inc.initialize();
var categoryFieldName = 'category';
inc.setValue(categoryFieldName, 'network'); // Set category using setValue
inc.setValue('short_description', 'Critical VPN Issue via setValue');
inc.insert();
gs.print('Created with setValue: Category is ' + inc.category + ' and issue is: ' + inc.short_description);Result: A new incident is created with values set using `setValue()`.
Updating Records: The 'U' in CRUD
18. Updating a Single Record with update()
To modify an existing record, you first retrieve it (using `get()` or a `query()` loop), change the desired fields, and then call `update()`.
var inc = new GlideRecord('incident');
// Replace 'INC0000057' with an actual incident number from your instance to update
if (inc.get('number', 'INC0000057')) {
gs.print('Updating Incident: ' + inc.number + '. Current state: ' + inc.state.getDisplayValue());
inc.setValue('state', 2); // Set state to 'In Progress' (value for In Progress is typically 2)
inc.update(); // Saves the changes to the database
gs.print('Incident ' + inc.number + ' updated to state: ' + inc.state.getDisplayValue());
} else {
gs.print('Incident INC0000057 not found for update.');
}Result: The specified incident's state will be changed to 'In Progress'.
19. Updating Multiple Records with updateMultiple()
This method is very powerful (and potentially dangerous if used carelessly!). It updates all records that match your query in one go, without the need for a `while` loop.
var inc = new GlideRecord('incident');
inc.addQuery('category', 'hardware'); // Find all incidents in the 'hardware' category
inc.addQuery('active', true); // Only active ones
gs.print('Attempting to update ' + inc.getRowCount() + ' active hardware incidents.'); // Always check count first!
inc.setValue('category', 'software'); // Change their category to 'software'
inc.setValue('short_description', 'Migrated from Hardware: ' + inc.short_description); // Add a note
inc.updateMultiple(); // Updates all matching records
gs.print('Multiple records updated.');Result: All active incidents with the category 'hardware' will have their category changed to 'software', and their short description updated.
Warning: Always run a `query()` and `getRowCount()` before `updateMultiple()` to verify the number of records that will be affected. This helps prevent accidental mass updates!
Deleting Records: The 'D' in CRUD
20. Deleting a Single Record with deleteRecord()
Similar to updating, you retrieve a single record and then call `deleteRecord()`.
var inc = new GlideRecord('incident');
// Replace 'INC0010013' with an actual incident number you want to delete (preferably one you just created for testing!)
if (inc.get('number', 'INC0010013')) {
var incidentNumberToDelete = inc.number;
inc.deleteRecord(); // Deletes the single record
gs.print('Incident ' + incidentNumberToDelete + ' has been deleted.');
} else {
gs.print('Incident INC0010013 not found for deletion.');
}Result: The specified incident record is permanently removed from the database.
21. Deleting Multiple Records with deleteMultiple()
The most powerful (and potentially most destructive) method. It deletes all records matching your query without iteration.
var inc = new GlideRecord('incident');
inc.addQuery('priority', '4'); // Find all incidents with priority 4 (Low)
inc.addQuery('active', false); // And are inactive
gs.print('Attempting to delete ' + inc.getRowCount() + ' inactive priority 4 incidents.');
inc.query(); // Must run query first to identify records
inc.deleteMultiple(); // Deletes all matching records
gs.print('Multiple records deleted.');Result: All inactive incidents with priority 4 will be permanently deleted.
Extreme Warning: `deleteMultiple()` is incredibly powerful. Always, *always* run a test with `getRowCount()` first to confirm exactly how many records will be deleted. Once deleted, they are gone (unless you have a database backup or strict retention policies). Use with extreme caution, especially in production environments!
Utility and Advanced Methods
22. ACL Checks: canCreate(), canRead(), canWrite(), canDelete()
These methods determine if the currently logged-in user (or the user impersonated by the script) has the necessary Access Control List (ACL) permissions to perform the respective operation on the table.
var inc = new GlideRecord('incident');
gs.print('Can create incident? ' + inc.canCreate());
gs.print('Can read incident? ' + inc.canRead());
gs.print('Can write (update) incident? ' + inc.canWrite());
gs.print('Can delete incident? ' + inc.canDelete());Result: Returns `true` or `false` for each operation based on the current user's roles and ACLs.
Interview Relevance: "How do you ensure your script respects ServiceNow's security model?" These methods are part of the answer, allowing you to build robust scripts that don't bypass ACLs.
23. isValid(), isValidField(), isValidRecord()
Useful for validation and preventing runtime errors.
isValid(): Checks if the table name provided to GlideRecord is valid.isValidField(): Checks if a specified field exists on the GlideRecord's table.isValidRecord(): Checks if a record was successfully retrieved by `get()` or `next()`.
var inc = new GlideRecord('incident');
gs.print('Is "incident" a valid table? ' + inc.isValid()); // Should be true
var nonExistentGr = new GlideRecord('uday_nonexistent_table'); // Example of a non-existent table
gs.print('Is "uday_nonexistent_table" a valid table? ' + nonExistentGr.isValid()); // Should be false
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 (unless you created it)
// After a query or get
if (inc.get('number', 'INC0000001')) { // Try to get an actual incident
gs.print('Is INC0000001 a valid record? ' + inc.isValidRecord()); // True
} else {
gs.print('Is INC0000001 a valid record? ' + inc.isValidRecord()); // False, as record not found
}Result: Boolean values indicating validity checks.
24. addNullQuery() and addNotNullQuery()
Filter records based on whether a field's value is empty or not.
Example: Incidents with an empty short description
var inc = new GlideRecord('incident');
inc.addNullQuery('short_description'); // Find incidents where short_description is empty
inc.setLimit(5);
inc.query();
while (inc.next()) {
gs.print('Incident with NULL short description: ' + inc.number);
}Result: Prints up to 5 incident numbers where the short description is null or empty.
Example: Incidents with a non-empty short description
var inc = new GlideRecord('incident');
inc.addNotNullQuery('short_description'); // Find incidents where short_description is NOT empty
inc.setLimit(5);
inc.query();
while (inc.next()) {
gs.print('Incident with NOT NULL short description: ' + inc.number);
}Result: Prints up to 5 incident numbers where the short description has a value.
25. autoSysFields(false) and setWorkflow(false)
These are advanced methods that give you fine-grained control over how updates are processed, but they should be used with caution!
autoSysFields(false): Prevents the system from automatically updating system fields like `sys_updated_on`, `sys_updated_by`, `sys_mod_count` when a record is updated via script. This is rare, but sometimes needed for data migration or specific integrations.setWorkflow(false): Prevents Business Rules (and thus potentially workflows, notifications, and other automated processes) from running when a record is updated via script. Use this when you want to perform a direct data change without triggering any of the platform's standard automation.
Important Notes:
autoSysFields(false)has limitations and may not work as expected in Scoped Applications.setWorkflow(false)effectively bypasses many of ServiceNow's core automation features. Understand the full impact before using it. This is a common source of bugs if not used correctly.
var inc = new GlideRecord('incident');
inc.addQuery('state', '1'); // Find incidents in 'New' state
inc.setLimit(2); // For testing, limit to a few records
inc.query();
while (inc.next()) {
gs.print('Updating Incident ' + inc.number + ' without sys_field updates or workflow...');
inc.autoSysFields(false); // Disable automatic system field updates
inc.setWorkflow(false); // Disable Business Rules and workflow
inc.setValue('state', '2'); // Change state to 'In Progress'
inc.update();
gs.print('Updated ' + inc.number);
}Result: The state of the specified incidents will be updated, but `sys_updated_on`, `sys_updated_by`, etc., will not change, and no Business Rules related to incident updates will execute.
26. addJoinQuery()
This method allows you to query one table based on conditions from a related table. It's like performing a SQL JOIN operation.
var prob = new GlideRecord('problem');
// Find problems that have at least one incident associated with them where
// the incident's 'opened_by' field matches the problem's 'caller_id' (or a similar link)
// Here, we'll simplify and say 'Find problems that have an associated incident record'.
// Let's assume problem records are linked to incidents via a reference field, or vice-versa.
// A common join is linking tables via sys_id or specific reference fields.
// For example, find problems related to incidents where the incident.problem_id = problem.sys_id
prob.addJoinQuery('incident', 'sys_id', 'problem_id'); // Join problem table with incident table
// prob.addJoinQuery('incident'); // Simpler join if no specific conditions
prob.query();
while (prob.next()) {
gs.print('Problem with associated Incident: ' + prob.number);
}Result: Prints the numbers of problem records that have one or more associated incidents. This is a powerful way to perform complex reporting or data manipulation across related tables.
27. getGlideObject().getNumericValue() and setAbortAction()
This example demonstrates how to validate dates and stop a record action if the validation fails. `getGlideObject()` returns the underlying GlideElement object, and `getNumericValue()` helps compare date/time fields. `setAbortAction(true)` prevents the current database operation (insert/update) from completing. This is typically used in Business Rules.
// This example assumes you are running inside a Business Rule (e.g., Before Insert/Update)
// and 'current' refers to the GlideRecord of the record being processed.
// Also assumes fields 'u_date1' and 'u_date2' exist on the table.
if (current.u_date1 && current.u_date2 && !current.u_date1.nil() && !current.u_date2.nil()) {
var start = current.u_date1.getGlideObject().getNumericValue(); // Get date as milliseconds
var end = current.u_date2.getGlideObject().getNumericValue();
if (start > end) {
gs.addInfoMessage('Start date must be before end date.'); // Display a message to the user
current.u_date1.setError('Start date must be before end date.'); // Highlight the field
current.setAbortAction(true); // Stop the current insert/update operation
}
} else {
gs.print('u_date1 or u_date2 are null or empty.');
}
Result: If this code were in a Business Rule, trying to save a record where `u_date1` is after `u_date2` would show an error message, highlight `u_date1`, and prevent the record from being saved.
Troubleshooting GlideRecord: When Things Go Wrong
Even the best developers run into issues. Here are some common troubleshooting tips for GlideRecord:
- Check your Table and Field Names: Typos are the most common culprits. Double-check table names (e.g., `incident`, `sys_user`) and field names (e.g., `short_description`, `sys_id`). ServiceNow field names are case-sensitive in scripts.
- Use
gs.info()(orgs.debug()): Sprinkle these throughout your script to print variable values, query counts (`getRowCount()`), or messages at different stages of execution. This is your primary debugging tool for server-side scripts. - Test Your Queries in a List View: For complex `addQuery()` chains or `addEncodedQuery()`, build the query in a list view first. This visually confirms your conditions and gives you the exact encoded query string.
- Verify ACLs: If your script isn't creating, reading, updating, or deleting as expected, it might be an ACL issue. Use `canCreate()`, `canRead()`, etc., or check the ACLs on the table and fields directly.
- Check the System Logs: For more detailed errors (especially if your script throws an exception), check "System Logs > All" for errors related to your script.
- Limit Your Results: When debugging a `query()` on a large table, always use `setLimit()` to avoid processing thousands of records and potentially crashing your browser or instance.
- Always Test in Dev! Seriously, we can't stress this enough.
Interview Relevance: Standing Out with GlideRecord Knowledge
Mastering GlideRecord isn't just about writing code; it's about understanding the core of ServiceNow data interaction. Interviewers frequently ask about GlideRecord to gauge a candidate's practical skills. Be prepared to answer:
- "What is GlideRecord and why is it important?"
- "Name the main CRUD operations and their corresponding GlideRecord methods."
- "What's the difference between
getValue()andgetDisplayValue()?" - "When would you use
addQuery()versusaddEncodedQuery()?" - "Explain the purpose of
setWorkflow(false)andautoSysFields(false)and why they should be used cautiously." - "How do you ensure data integrity and prevent accidental data loss when using GlideRecord?" (Think testing, `getRowCount()`, `setLimit()`, ACLs).
- "What's the difference between client-side and server-side scripting, and where does GlideRecord fit in?"
Conclusion: Your Journey to GlideRecord Mastery
Phew! We've covered a lot of ground today. GlideRecord is truly the backbone of server-side scripting in ServiceNow, enabling you to build dynamic, data-driven applications that extend the platform's capabilities. From simply reading a few records to performing complex data migrations, GlideRecord gives you the power.
The key to mastery, like any skill, is practice. Keep experimenting in your personal developer instance, build small scripts, and challenge yourself to solve real-world problems using the methods we've discussed. Don't be afraid to make mistakes – that's often the best way to learn!
By understanding GlideRecord's architecture, its diverse methods, and best practices for its use, you're not just writing code; you're becoming a more proficient and confident ServiceNow developer. Happy coding!