Mastering Data Interaction: A Deep Dive into ServiceNow GlideRecord and Record Class Names
In the vast universe of enterprise software, data is the undisputed king. Whether you’re building a new application, automating a workflow, or simply ensuring your service desk runs smoothly, interacting with data is at the heart of it all. For ServiceNow developers, this interaction often begins and ends with a powerful set of tools known as Glide APIs.
Today, we’re going to pull back the curtain on one of the most fundamental and frequently used server-side APIs: GlideRecord. While the topic might sound a bit formal as “Understanding Record Class Names,” in the practical world of ServiceNow, a “record class name” is simply the table name you want to work with. Think of GlideRecord as your Swiss Army knife for manipulating data directly in the ServiceNow database, and the “record class name” as the specific type of record (e.g., Incident, User, Problem) you’re targeting. We’ll explore its architecture, essential methods, and real-world applications, ensuring you’re well-equipped to manage data like a pro. We’ll even touch upon its client-side cousin, GlideForm, for a complete picture.
Ready to transform your ServiceNow scripting capabilities? Let’s dive in!
The ServiceNow API Landscape: A Quick Overview
ServiceNow isn’t just a platform; it’s an ecosystem. To truly make it your own, to customize existing functionality, or to change its default behavior, you need to speak its language. That’s where Glide APIs come into play. These are a set of robust classes and methods that provide developers with a structured, flexible way to interact with ServiceNow applications through scripting.
Imagine needing to pull information from a user profile or update the status of an incident. Without Glide APIs, you’d be grappling with complex SQL queries, direct database interactions, and a whole lot of security headaches. Glide APIs abstract all that away, letting you perform powerful database operations using familiar JavaScript syntax.
Client-Side vs. Server-Side: Knowing Your Playground
Before we zero in on GlideRecord, it’s crucial to understand the two main stages where scripting happens in ServiceNow:
- Client-Side APIs: These run directly in the user’s browser. They’re all about enhancing the user interface (UI), validating form fields in real-time, and creating dynamic experiences. Think instant feedback, hiding/showing fields, or populating dropdowns based on user selections. Examples include
GlideForm (g_form),GlideUser (g_user), andGlideAjax. - Server-Side APIs: These execute on the ServiceNow server itself. This is where the heavy lifting happens – interacting with the database, performing complex calculations, integrating with external systems, and enforcing business logic. This is GlideRecord’s domain, alongside APIs like
GlideSystem (gs)andGlideDateTime.
GlideRecord: Your Command Center for Server-Side Data
If you’re doing anything significant with data on the ServiceNow server, you’re almost certainly using GlideRecord. It’s not just common; it’s foundational. Think of it as the most important API for any ServiceNow developer aiming to truly manipulate records.
What Exactly is GlideRecord?
At its core, GlideRecord is a special Java class (accessed via native JavaScript) that runs exclusively on the server side. Its primary purpose? To enable CRUD operations – Create, Read, Update, and Delete – on records within your ServiceNow instance. Instead of writing cumbersome SQL queries, you use GlideRecord methods to interact with the underlying database, handling both rows (records) and columns (fields) with ease.
In ServiceNow, direct SQL interaction with the database is a no-go for security and architectural reasons. GlideRecord provides a safe, efficient, and platform-native way to achieve the same results, ensuring data integrity and adhering to platform best practices.
Key Characteristics of GlideRecord:
- Most Common API: You’ll encounter it in Business Rules, Script Includes, Workflows, Fix Scripts, and more.
- Server-Side Execution: It runs where the data lives, ensuring efficient and secure operations.
- SQL Generation: It intelligently translates your JavaScript methods into optimized SQL queries behind the scenes.
- CRUD Operations: The full suite of data manipulation is at your fingertips.
GlideRecord Architecture: JavaScript Meets Your Database
The beauty of GlideRecord lies in its abstraction. You write clean, readable JavaScript code, and GlideRecord takes care of the intricate details of talking to the database. When you instantiate a new GlideRecord('incident'), for instance, you’re telling the platform you want to work with the ‘incident’ table. Every method you call on that GlideRecord object (like addQuery(), query(), update()) is then translated into appropriate SQL commands, executed, and the results are returned to your script as JavaScript objects.
This “API Mapping” effectively bridges the gap, allowing developers to focus on business logic rather than database specifics.
The Golden Rule: Test Before You Deploy!
Working with GlideRecord is powerful, which means it demands respect. A small error can have significant consequences. Always, always test your GlideRecord scripts on a non-production instance first!
An incorrectly constructed query, like referencing an invalid field name, can lead to an invalid database query. Running insert(), update(), or deleteRecord() methods on such a bad query could potentially result in unintended data loss or corruption. Don’t risk it – validate your scripts thoroughly.
Navigating Data: Essential GlideRecord Methods
GlideRecord comes packed with a vast array of methods. Instead of listing every single one, let’s group them by their common use cases and highlight the ones you’ll use most frequently. Remember, you’ll typically execute these in the “Script – Background” module for testing or within server-side scripts like Business Rules or Script Includes.
Initializing and Querying Records
This is where you tell GlideRecord which table to interact with and define the records you want to retrieve.
new GlideRecord('table_name'): The fundamental step. This creates a new GlideRecord object for a specified table. The ‘table_name’ here is your “record class name.”var inc = new GlideRecord('incident'); // 'incident' is the record class name // Now 'inc' represents the incident table in our scriptquery(): Executes the query you’ve built. Without this, youraddQuery()calls are just declarations.inc.query();addQuery('field_name', 'value')oraddQuery('field_name', 'operator', 'value'): Adds a condition to your query. You can chain multipleaddQuery()calls, which act as AND conditions.inc.addQuery('priority', '1'); // Equivalent to priority = 1 inc.addQuery('category', 'software'); // Equivalent to category = softwarePro-Tip: Get familiar with common operators:
=,!=,>,>=,<,<=,IN,NOT IN,STARTSWITH,ENDSWITH,CONTAINS,DOES NOT CONTAIN.addEncodedQuery('encoded_query_string'): A powerful method for applying complex queries. You can build these visually from a list view and copy the generated query string.var encodedQuery = 'active=true^category=software^priority=1'; inc.addEncodedQuery(encodedQuery);addActiveQuery(): A shorthand foraddQuery('active', true). Very useful for common scenarios.addInactiveQuery(): The opposite ofaddActiveQuery(), effectivelyaddQuery('active', false).orderBy('field_name')/orderByDesc('field_name'): Sorts your results in ascending or descending order.inc.orderByDesc('short_description');setLimit(number): Restricts the number of records returned by the query. Essential for performance, especially when you only need a few results.inc.setLimit(10); // Only retrieve the first 10 recordschooseWindow(offset, limit): Allows you to retrieve a specific "window" of records, useful for pagination. The first value is inclusive, the second is exclusive.inc.chooseWindow(3, 7); // Gets records from index 3 up to (but not including) 7addNullQuery('field_name')/addNotNullQuery('field_name'): Filters records based on whether a specific field's value is null or not.inc.addNullQuery('assigned_to'); // Finds incidents not assigned to anyoneaddJoinQuery('join_table', 'primary_field', 'secondary_field'): Performs a join operation between two tables, allowing you to query based on related records.var prob = new GlideRecord('problem'); prob.addJoinQuery('incident', 'opened_by', 'caller_id'); // Finds problems linked to incidents where opened_by matches caller_id prob.query(); while(prob.next()){ gs.print(prob.number); }
Retrieving and Iterating Through Data
Once you've queried, these methods help you access the data.
next(): Moves the iterator to the next record in the result set. Crucial for looping through multiple records.while (inc.next()) { gs.print(inc.number + ' - ' + inc.short_description); }get('sys_id')orget('field_name', 'value'): Retrieves a single record based on its sys_id or a specific field/value pair.var inc = new GlideRecord('incident'); inc.get('INC0009005'); // Get by display value (if unique) // OR inc.get('number', 'INC0009005'); // Get by specific field gs.print(inc.sys_id);getValue('field_name'): Returns the actual database value of a field. For reference fields, this will be the sys_id.var categoryValue = inc.getValue('category');getDisplayValue('field_name'): Returns the user-friendly display value of a field, especially useful for reference fields or choice lists.var priorityDisplay = inc.priority.getDisplayValue(); // Accessing field directly, then its display value // OR for non-reference fields var categoryDisplay = inc.getDisplayValue('category');getRowCount(): Returns the total number of records found by the query.inc.query(); gs.print('Total incidents: ' + inc.getRowCount());getTableName(): Returns the "record class name" (table name) associated with the current GlideRecord object.var gr = new GlideRecord('change_request'); gs.print(gr.getTableName()); // Output: change_requestgetUniqueValue(): Returns the unique identifier for the current record, which is typically the sys_id.inc.query(); inc.next(); gs.print(inc.getUniqueValue());getEncodedQuery(): Returns the encoded query string that was used to generate the current result set. Useful for debugging.inc.addQuery('active', true); inc.query(); gs.print(inc.getEncodedQuery()); // Output: active=truegetRecordClassName(): Retrieves the class name (table name) for the current record. Similar togetTableName().var gr = new GlideRecord('sys_user'); gs.info(gr.getRecordClassName()); // Output: sys_user
Creating and Updating Records
The heart of data manipulation!
initialize(): Prepares a new, empty record for insertion. It's like opening a blank form.var inc = new GlideRecord('incident'); inc.initialize();newRecord(): A newer method that combinesinitialize()and sets default values, also assigning a unique ID.var inc = new GlideRecord('incident'); inc.newRecord(); // Ready for populating fields and insertingfield_name = 'value'orsetValue('field_name', 'value'): Assigns a value to a field. Direct assignment (inc.category = 'network') is common and usually preferred for readability.setValue()is useful when the field name is dynamic (e.g., from a variable).inc.category = 'network'; inc.setValue('short_description', 'Critical VPN Issue');insert(): Saves the new record (created withinitialize()ornewRecord()) into the database.inc.insert(); // The new incident record is now created gs.print('New incident created: ' + inc.number);update(): Saves changes to an existing record. You must first retrieve the record (e.g., withget()orquery()andnext()), modify its fields, and then callupdate().var inc = new GlideRecord('incident'); if (inc.get('INC0000057')) { // Find the record inc.state = 2; // Set state to 'Active' inc.update(); // Save the changes gs.print('Incident ' + inc.number + ' updated.'); }updateMultiple(): Updates all records that match the current query with the specified field changes. Very powerful but use with extreme caution!var inc = new GlideRecord('incident'); inc.addQuery('category', 'hardware'); inc.setValue('category', 'software'); // Change category from hardware to software inc.updateMultiple(); // All matching records are updated
Deleting Records
Use these methods with the utmost care, especially in production environments.
deleteRecord(): Deletes the current record (the one you've retrieved withget()or currently iterated to withnext()).var inc = new GlideRecord('incident'); if (inc.get('number', 'INC0010013')) { inc.deleteRecord(); gs.print('Incident ' + inc.number + ' deleted.'); }deleteMultiple(): Deletes all records that match the current query.var inc = new GlideRecord('incident'); inc.addQuery('priority', 4); // Find all priority 4 incidents inc.query(); inc.deleteMultiple(); // Delete all of them!
Validation, Permissions, and Utility Methods
These methods help you check conditions and retrieve meta-information.
isNewRecord(): Returns true if the current record has been initialized but not yet inserted into the database.var inc = new GlideRecord('incident'); inc.newRecord(); gs.print('Is it a new record? ' + inc.isNewRecord()); // Output: trueisValid(): Checks if the GlideRecord object was successfully initialized with a valid table name.var grIncident = new GlideRecord('incident'); gs.print('Incident table exists: ' + grIncident.isValid()); // Output: true var grInvalid = new GlideRecord('non_existent_table'); gs.print('Non-existent table exists: ' + grInvalid.isValid()); // Output: falseisValidField('field_name'): Checks if a specified field exists in the current table.var inc = new GlideRecord('incident'); gs.print('Category field exists: ' + inc.isValidField('category')); // Output: trueisValidRecord(): Determines if a record was actually returned by a query orget()operation.var inc = new GlideRecord('incident'); inc.get('number', 'INC0010012'); gs.print('Incident ' + inc.number + ' exists: ' + inc.isValidRecord());canCreate(),canRead(),canWrite(),canDelete(): These methods check if the current user (running the script) has the necessary Access Control List (ACL) permissions to perform the respective operation on the record or table. Critical for security!var inc = new GlideRecord('incident'); gs.print('Can I create incidents? ' + inc.canCreate());autoSysFields(false): Prevents the automatic update of system fields (likesys_updated_on,sys_updated_by,sys_mod_count) when anupdate()orinsert()is called. Use with extreme caution, typically for data migrations. (Note: Not always effective in scoped apps.)inc.autoSysFields(false); // Do not update system fieldssetWorkflow(false): Prevents business rules and workflow engines from running when the record is updated or inserted. Again, use sparingly and with full understanding of potential side effects.inc.setWorkflow(false); // Do not trigger business rules or workflowsCombined Example (autoSysFields & setWorkflow):
var inc = new GlideRecord ('incident'); inc.addQuery ('state', 1); // Find New incidents inc.query (); while (inc.next ()) { inc.autoSysFields (false); // Don't update 'Updated' fields inc.setWorkflow (false); // Don't run business rules inc.setValue ('state', 2); // Change state to 'Active' inc.update (); }getLink(false): Retrieves a direct link to the current record.var gr = new GlideRecord('incident'); gr.query(); gr.next(); gs.print(gs.getProperty('glide.servlet.uri') + gr.getLink(false)); // Provides a clickable URLgetElement('field_name'): Returns the specified column (GlideElement object) of the current record.var inc = new GlideRecord('incident'); inc.initialize(); inc.setValue('short_description', 'I am facing VPN Problem'); inc.insert(); gs.print(inc.getElement('short_description')); // Prints the GlideElement object, which will implicitly convert to its value for gs.printsetAbortAction(true): Typically used in before Business Rules to prevent the current database operation (insert, update, delete) from completing. Useful for validation.if (current.u_start_date > current.u_end_date) { gs.addInfoMessage('Start date must be before end date'); current.setAbortAction(true); // Stop the record from being saved }
GlideRecord in Action: Practical Exercises
The best way to learn is by doing! Remember to use the "Script - Background" module for testing these server-side scripts.
Exercise 1: Printing All Incident Numbers
This demonstrates the basic query and iteration pattern.
var inc = new GlideRecord('incident'); // Instantiate GlideRecord for 'incident' table
inc.query(); // Execute the query (no conditions means all records)
while (inc.next()) { // Loop through each record found
gs.print(inc.number); // Print the 'number' field of the current incident
}
// Result: Prints all incident numbers
Exercise 2: Finding High Priority Software Incidents
Using multiple addQuery() methods.
var inc = new GlideRecord('incident');
inc.addQuery('active', true); // Condition 1: Incident is active
inc.addQuery('priority', '1'); // Condition 2: Priority is 1 (Critical)
inc.addQuery('category', 'software'); // Condition 3: Category is software
inc.query();
while(inc.next()){
gs.print(inc.number + ' - ' + inc.short_description);
}
// Result: Prints active, critical, software-related incidents
Exercise 3: Creating a New Incident
Showcasing initialize() and insert().
var inc = new GlideRecord('incident');
inc.initialize(); // Get a blank incident record
inc.category = 'network'; // Set field values
inc.short_description = 'Firewall connectivity issue in Data Center A';
inc.priority = 1;
var newSysId = inc.insert(); // Insert the new record and get its sys_id
gs.print('New Incident ' + inc.number + ' created with Sys ID: ' + newSysId);
// Result: A new incident record is created in the system, and its number and sys_id are printed.
Exercise 4: Updating an Existing Incident's State
Demonstrating retrieving and updating a specific record.
var inc = new GlideRecord('incident');
// Assuming INC0000057 exists and we want to change its state to 'Resolved' (value 3)
if (inc.get('number', 'INC0000057')) {
inc.state = 3; // Set the state field to 'Resolved' (value typically 3 or 7 depending on instance)
inc.update();
gs.print('Incident ' + inc.number + ' state updated to Resolved.');
} else {
gs.print('Incident INC0000057 not found.');
}
// Result: The specified incident's state is updated.
Beyond Server-Side: A Glimpse at GlideForm (Client-Side Interactions)
While GlideRecord is your powerhouse for server-side data, you'll often need to make dynamic changes to the forms users interact with in their browsers. That's where GlideForm, accessed via the global object g_form, comes in. It's a client-side API primarily used in Client Scripts and Catalog Client Scripts to modify the appearance and behavior of the current record's form view.
Think of GlideRecord manipulating data in the database, and GlideForm manipulating how that data is displayed and interacted with on the screen.
Key GlideForm Methods:
g_form.setValue('field_name', 'value'): Sets the value of a form field.g_form.setValue('category', 'hardware');g_form.getValue('field_name'): Gets the current value of a form field.alert(g_form.getValue('category'));g_form.setMandatory('field_name', true/false): Makes a field mandatory or optional.g_form.setReadOnly('field_name', true/false)org_form.setDisabled('field_name', true/false): Makes a field read-only.g_form.setDisplay('field_name', true/false): Hides or shows a field, consuming space.g_form.setVisible('field_name', true/false): Hides or shows a field, without consuming space.g_form.addInfoMessage('message')/g_form.addErrorMessage('message'): Displays messages to the user on the form.
Important Best Practice: While g_form.setMandatory(), setDisplay(), and setReadOnly() can be used in Client Scripts, for static or condition-based UI changes, UI Policies are generally the preferred and best practice approach. They offer better performance and are easier to manage for form-level UI changes.
Troubleshooting Common GlideRecord Issues
Even seasoned developers hit snags. Here are some common issues and how to approach them:
- "Table not found" / Invalid table name: Double-check your table name in
new GlideRecord('your_table_name'). Usegr.isValid()to confirm the table exists. - "Field not found" / Invalid field name: Typos happen! Ensure your field names (e.g., in
addQuery(),getValue()) are correct. Usegr.isValidField('your_field_name')to verify. - No records returned:
- Did you call
gr.query()beforewhile(gr.next())? - Are your
addQuery()conditions too restrictive or incorrect? Usegs.print(gr.getEncodedQuery())to see the exact query being executed. - Check your data! Does a record matching your conditions actually exist?
- Did you call
- Infinite loops: Typically happens if you forget
gr.next()inside yourwhileloop. The loop condition never changes, so it just keeps processing the same record. - Performance issues (slow scripts):
- Are you querying too many records? Use
setLimit()if you only need a few. - Are your queries specific enough? Broad queries without conditions can scan entire tables.
- Are you calling
query()andnext()inside another loop? This can be very inefficient.
- Are you querying too many records? Use
- Updates/Inserts not working:
- Did you call
gr.update()orgr.insert()? - Are there ACLs preventing the current user from performing the action? Use
gr.canWrite(),gr.canCreate(), etc. - Are Business Rules or Workflows aborting the action? Check server-side logs.
- Did you call
- Debugging: Use
gs.print()(for "Script - Background") orgs.info(),gs.debug(),gs.warn(),gs.error()(for other server-side scripts) to output variables and progress messages to the system logs.
Interview Relevance: Be Prepared!
GlideRecord is a staple in ServiceNow technical interviews. Expect questions like:
- "Explain the difference between client-side and server-side scripting in ServiceNow, and give examples of APIs for each."
- "What is GlideRecord and why is it so important?"
- "How do you perform CRUD operations using GlideRecord? Provide code examples."
- "When would you use
addQuery()versusaddEncodedQuery()?" - "What's the purpose of
query()andnext()?" - "Explain
autoSysFields(false)andsetWorkflow(false). When would you use them?" - "How do you ensure your GlideRecord scripts are performant?" (Think
setLimit(), specific queries, avoiding nested GlideRecord queries). - "What are some common pitfalls when working with GlideRecord?" (Think ACLs, forgetting
update()/insert(), infinite loops).
Conclusion: Empowering Your ServiceNow Development
Understanding GlideRecord is non-negotiable for any serious ServiceNow developer. It's the primary interface for interacting with your instance's data, allowing you to build robust, dynamic, and integrated solutions. By mastering its methods, you gain the power to create, read, update, and delete records with precision and control.
Remember that a "record class name" is simply the table you're targeting, and GlideRecord provides the object-oriented structure to interact with instances of that class. Combine this server-side prowess with client-side GlideForm for a complete toolkit that will truly empower your ServiceNow development journey.
Keep practicing, keep exploring the documentation, and you'll soon be navigating the ServiceNow data landscape like a seasoned pro!