GlideRecord vs. GlideAjax: Mastering ServiceNow’s Server and Client-Side Scripting Pillars
If you’ve spent any time developing in ServiceNow, you’ve undoubtedly bumped into the “Glide” family of APIs. These powerful classes are the bedrock of most customizations, allowing us to bend and shape the platform’s default behavior to fit complex business needs. They empower us to interact with the underlying database, manipulate forms, manage users, and so much more, all without ever writing a direct SQL query.
Among these invaluable tools, two names stand out prominently: GlideRecord and GlideAjax. They are fundamental, frequently used, and often a source of confusion for new and even intermediate developers. Understanding their distinct roles and when to employ each is absolutely crucial for writing efficient, secure, and maintainable ServiceNow code.
Let’s embark on a journey to demystify these two titans, exploring their purpose, where they live, how they operate, and when they shine brightest in your development toolkit.
Introduction: Unlocking ServiceNow with Glide APIs
ServiceNow developers often leverage Glide APIs to customize existing functionalities and change the application’s default behavior. These Glide Classes provide immense flexibility, allowing interaction with the ServiceNow application through scripting. Instead of writing raw SQL queries, we use Glide APIs to perform database operations, making our scripts more robust and platform-friendly. Each API comes packed with numerous methods, each designed to perform specific operations within the ServiceNow ecosystem.
The Power of Glide Classes: A Quick Glimpse
Before we deep-dive, it’s good to remember that Glide APIs are categorized based on where they primarily execute:
| Client-Side APIs | Server-Side APIs |
|---|---|
| GlideForm (`g_form`) | GlideRecord |
| GlideUser (`g_user`) | GlideSystem (`gs`) |
| GlideAjax | GlideDate |
| GlideDialogWindow | GlideDate and Time |
| GlideList | GlideAggregation |
| GlideMenu | GlideElement |
As you can see, GlideRecord sits squarely on the server-side, while GlideAjax is initiated on the client-side. This fundamental distinction is our starting point.
Meet GlideRecord: The Server-Side Database Whisperer
If you’re looking to directly interact with ServiceNow’s database from a script, GlideRecord is your best friend. It’s arguably the most common and vital API in the platform, frequently used in almost every custom solution.
What is GlideRecord?
Think of GlideRecord as ServiceNow’s sophisticated object-relational mapping (ORM) layer. It’s a special JavaScript class that abstracts away the complexities of direct database interaction. Instead of writing SQL queries, you manipulate records (rows) and fields (columns) programmatically using GlideRecord methods. It acts as a bridge between your script and the underlying ServiceNow database tables.
Where Does GlideRecord Live? (Server-Side Execution)
This is a critical point: GlideRecord runs exclusively on the server-side. What does that mean in practice? It means you’ll find GlideRecord instances and operations within:
- Business Rules: Executing before or after database operations (insert, update, delete).
- Script Includes: Often used as helper functions callable from other server-side scripts or via GlideAjax.
- Fix Scripts: For one-off data manipulations or migrations.
- Scheduled Jobs: For recurring tasks that need to interact with data.
- Workflow Activities: Custom activities within workflows.
- UI Actions (Server-side scripts): When you need to update records directly upon a button click.
You cannot directly use GlideRecord in client-side scripts (like `g_form` scripts) because client-side code runs in the user’s browser, and direct database access from a browser is a major security risk and architectural no-go.
Performing CRUD Operations with GlideRecord (Examples)
GlideRecord’s superpower is its ability to perform the fundamental CRUD operations on any table in the ServiceNow database. Let’s look at some practical examples.
Querying Records: The “Read” in CRUD
This is how you fetch data from the database. It’s like asking the database, “Show me all active incidents that are critical.”
var grIncident = new GlideRecord('incident'); // Initialize GlideRecord for the 'incident' table
grIncident.addQuery('active', true); // Add a query condition: active = true
grIncident.addQuery('priority', '1'); // Add another condition: priority = 1 (Critical)
grIncident.query(); // Execute the query
// Iterate through the results
while (grIncident.next()) {
gs.info('Incident Number: ' + grIncident.number + ', Short Description: ' + grIncident.short_description);
// You can access any field on the record directly: grIncident.field_name
}
Note on Encoded Queries: For more complex queries, you can use an encoded query string, which is what the filter builder generates:
var encodedQuery = 'active=true^priority=1';
var grIncident = new GlideRecord('incident');
grIncident.addEncodedQuery(encodedQuery);
grIncident.query();
while (grIncident.next()) {
gs.info('Incident Number (encoded query): ' + grIncident.number);
}
Creating New Records: The “Insert”
This is how you add a new row to a table.
var grNewTask = new GlideRecord('task'); // Initialize for the 'task' table
grNewTask.initialize(); // Prepares a new, empty record object
grNewTask.short_description = 'New task created by script';
grNewTask.description = 'This task was automatically generated by a server-side script.';
grNewTask.assignment_group.setDisplayValue('Service Desk'); // Set by display value
grNewTask.assigned_to = '62826a0edb35130089e93310ffe961c0'; // Set by sys_id (more robust)
var newSysId = grNewTask.insert(); // Insert the record and capture its sys_id
if (newSysId) {
gs.info('New task created with sys_id: ' + newSysId + ' and number: ' + grNewTask.number);
} else {
gs.error('Failed to create new task.');
}
Updating Existing Records: The “Update”
To modify an existing record, you first query it, change its field values, and then call `update()`.
var grIncident = new GlideRecord('incident');
grIncident.addQuery('number', 'INC0000001'); // Find a specific incident
grIncident.query();
if (grIncident.next()) { // If the incident is found
grIncident.state = '6'; // Set state to 'Resolved'
grIncident.close_notes = 'Resolved by script automation.';
grIncident.update(); // Save the changes
gs.info('Incident ' + grIncident.number + ' updated successfully.');
} else {
gs.warn('Incident INC0000001 not found for update.');
}
Deleting Records: The “Delete”
Be extremely cautious with deletion! Always test thoroughly.
var grOldTasks = new GlideRecord('task');
grOldTasks.addQuery('state', '7'); // Find all 'Closed Complete' tasks
grOldTasks.addQuery('sys_created_on', '<javascript:gs.daysAgo(365)'); // Older than 1 year
grOldTasks.query();
var deletedCount = 0;
while (grOldTasks.next()) {
// IMPORTANT: It's good practice to log or get confirmation before deleting
gs.info('Deleting task: ' + grOldTasks.number);
grOldTasks.deleteRecord(); // Delete the current record
deletedCount++;
}
gs.info('Total tasks deleted: ' + deletedCount);
Note: `deleteMultiple()` is more efficient for deleting many records without iterating through them individually, but it skips Business Rules that run on `delete` (unless explicitly configured to run for `deleteMultiple`). `deleteRecord()` ensures all delete Business Rules are triggered.
GlideRecord Best Practices and Pitfalls
- Always Query First for Updates/Deletes: Never call `update()` or `deleteRecord()` without a `query()` first, unless you’re confident you’re working with a pre-existing GlideRecord object.
- Use `initialize()` for New Records: Always use `new GlideRecord(‘table’).initialize()` before setting fields and calling `insert()`.
- Test Queries on Non-Production: This cannot be stressed enough. An incorrectly constructed query, especially one with an invalid field name, can lead to invalid results. Running `insert()`, `update()`, or `deleteRecord()` on a bad query can result in unintended data loss or corruption. Always, always test your queries in development or testing instances!
- Limit Results: For queries that might return many records, use `gr.setLimit(number)` to prevent performance issues.
- Dot-walking: You can often access related fields using dot-walking (e.g., `grIncident.caller_id.name`), but be mindful of performance implications for large result sets.
- Avoid `autoSysFields(false)`: Unless absolutely necessary for specific integrations, let ServiceNow manage system fields like `sys_created_on` and `sys_updated_on`.
- Use `addActiveQuery()`: A convenient shorthand for `addQuery(‘active’, true)`.
- Secure Your Scripts: Ensure that scripts performing CRUD operations have appropriate permissions and only modify data they are intended to.
Interview Corner: GlideRecord Essentials
If you’re ever asked about GlideRecord, remember these key points:
- “It’s ServiceNow’s way of interacting with the database from the server-side.”
- “It performs CRUD operations without writing raw SQL.”
- “It’s used extensively in Business Rules, Script Includes, Workflows, and Scheduled Jobs.”
- “Always mention the importance of `query()`, `next()`, `insert()`, `update()`, and `deleteRecord()`.”
- “Highlight the warning about testing on non-production instances to prevent data loss.”
Enter GlideAjax: The Client’s Bridge to the Server
Now, let’s switch gears and move to the client-side. What if your user is filling out a form in their browser, and you need to fetch some data from the server or run some complex server-side logic without submitting the entire form and refreshing the page? That’s where GlideAjax comes in.
What is GlideAjax?
GlideAjax is a client-side API that allows you to make asynchronous calls to the server to execute server-side scripts (specifically, Script Includes) and retrieve data. Think of it as a secure, controlled portal from the user’s browser directly to the ServiceNow server’s backend processing capabilities.
It’s called “Ajax” because it uses Asynchronous JavaScript and XML (or JSON in modern implementations) to communicate with the server in the background, without interrupting the user’s current interaction with the form.
Where Does GlideAjax Live? (Client-Side Initiation, Server-Side Execution)
This is the crucial distinction from GlideRecord: GlideAjax is initiated from the client-side, but it executes code on the server-side.
- Initiated from: Client Scripts, UI Policies (sometimes via custom script), UI Pages (client script section).
- Executes code in: A Script Include (specifically, a class extending `AbstractAjaxProcessor`).
So, a client script says, “Hey server, I need some info!” and GlideAjax carries that message to a dedicated Script Include on the server, which then processes the request (often using GlideRecord itself!) and sends back a response.
How GlideAjax Works: The Client Script & Script Include Duo
GlideAjax always works in tandem with a Script Include. It’s a two-part conversation:
Step 1: The Client Script (Making the Call)
Your client script creates a GlideAjax object, specifies which Script Include and method to call, passes any necessary parameters, and then sends the request to the server.
// Example: Client Script (e.g., onChange for 'caller_id')
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
var ga = new GlideAjax('MyCustomAjaxUtils'); // Name of your Script Include
ga.addParam('sysparm_name', 'getCallerDetails'); // Name of the function in your Script Include
ga.addParam('sysparm_caller_id', newValue); // Pass a parameter to the server
ga.getXML(getResponse); // Asynchronous call, pass callback function
function getResponse(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
if (answer) {
var details = JSON.parse(answer); // Assuming server returns JSON
g_form.setValue('caller_email', details.email);
g_form.setValue('caller_phone', details.phone);
}
}
}
Step 2: The Script Include (Answering the Call)
The Script Include (which must be set to ‘Client callable’ and typically extends `AbstractAjaxProcessor`) receives the request, processes it, and returns a response.
// Example: Script Include named 'MyCustomAjaxUtils'
// Must be 'Client callable: true'
// Must extend 'AbstractAjaxProcessor'
var MyCustomAjaxUtils = Class.create();
MyCustomAjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getCallerDetails: function() {
var callerSysId = this.getParameter('sysparm_caller_id');
var result = {};
if (callerSysId) {
var grUser = new GlideRecord('sys_user');
if (grUser.get(callerSysId)) { // Use get() to fetch a single record by sys_id
result.email = grUser.email + ''; // Convert to string
result.phone = grUser.phone + '';
}
}
// Return the answer as a JSON string (best practice)
return JSON.stringify(result);
},
// A common interview question: How do you pass parameters and retrieve them?
// Client-side: ga.addParam('sysparm_my_param', 'value');
// Server-side (Script Include): this.getParameter('sysparm_my_param');
_privateFunction: function() {
// Functions starting with '_' are considered private and cannot be called directly by GlideAjax
gs.info("This is a private function.");
},
type: 'MyCustomAjaxUtils' // Important for Script Includes
});
Notice how the Script Include (server-side) uses GlideRecord to fetch user details. This is a very common pattern: GlideAjax facilitates the client-server communication, and GlideRecord does the actual database heavy lifting on the server.
Practical GlideAjax Scenarios (Examples)
Scenario 1: Populating a Field Dynamically
Imagine you have a ‘Company’ field and you want to automatically populate the ‘Company Website’ field when a user selects a company. This prevents the user from manually typing the URL.
- Client Script (onChange for ‘company’ field): Calls GlideAjax, passing the selected company’s sys_id.
- Script Include: Receives the sys_id, uses GlideRecord to query the `core_company` table, retrieves the website URL, and returns it.
- Client Script (callback): Receives the URL and sets it in the ‘company_website’ field.
Scenario 2: Server-Side Validation on the Client
You need to check if a proposed ‘Short Description’ for an Incident already exists in the system before the user submits the form. This is a server-side check because it involves querying the database, but you want immediate feedback for the user.
- Client Script (onBlur for ‘short_description’ field): Calls GlideAjax, passing the entered short description.
- Script Include: Receives the description, uses GlideRecord to query active incidents with that description. Returns ‘true’ if a duplicate is found, ‘false’ otherwise.
- Client Script (callback): If ‘true’, displays an alert to the user and perhaps clears the field or marks it as invalid.
Synchronous vs. Asynchronous GlideAjax: A Crucial Distinction
GlideAjax offers two primary ways to send a request:
- `getXML(callbackFunction)` (Asynchronous – Recommended): The client script sends the request and continues executing other code immediately. When the server responds, the specified `callbackFunction` is executed. This prevents the browser from freezing and provides a much better user experience.
- `getXMLWait()` (Synchronous – Avoid where possible): The client script sends the request and then pauses, waiting for the server’s response before executing any further client-side code. This blocks the user interface and can lead to a perceived “frozen” browser, especially with slow server responses. It’s generally deprecated and should only be used in very specific, rare scenarios where subsequent client-side logic absolutely *must* have the server’s response before proceeding, and the performance impact is acceptable (e.g., very small, fast lookups during form load).
- `getXMLAnswer(callbackFunction)` (Asynchronous Shortcut): Similar to `getXML()`, but it directly gives you the `answer` attribute from the XML response in the callback, simplifying code if you only need that one piece of data.
Always strive for asynchronous calls. Modern web development prioritizes responsive UIs, and `getXMLWait()` goes against that principle.
GlideAjax Best Practices and Troubleshooting
- Asynchronous First: Prioritize `getXML()` or `getXMLAnswer()` for a smooth user experience.
- Define Script Includes Clearly: Ensure your Script Include has `Client callable: true` and extends `AbstractAjaxProcessor`.
- Use `sysparm_name` for Methods: Always use `ga.addParam(‘sysparm_name’, ‘yourMethodName’)` to specify which function in your Script Include to execute. This is a standard.
- Parameter Naming: Use `sysparm_` prefix for any custom parameters you pass (e.g., `sysparm_caller_id`). This helps distinguish them from system parameters.
- Return Structured Data: In your Script Include, return data as JSON strings (`JSON.stringify(yourObject)`) rather than raw strings or XML. It’s much easier to parse on the client-side using `JSON.parse()`.
- Error Handling: Implement checks for `answer` in your client-side callback, as the server might return an empty or error response.
- Debugging:
- Client-side: Use browser developer tools (console.log, network tab to see request/response).
- Server-side (Script Include): Use `gs.log()` or `gs.info()` within your Script Include and check System Logs (`syslog.list`) for output.
- Network Tab: The browser’s network tab is your best friend. Look for the XHR request to `xmlhttp.do` to inspect the request payload, headers, and the server’s response XML/JSON.
- Security: Be mindful of what data you expose from the server to the client. Validate all inputs coming from the client-side within your Script Include.
Interview Corner: GlideAjax Deep Dive
When asked about GlideAjax, make sure you hit these points:
- “It’s for client-side scripts to interact with server-side logic asynchronously.”
- “It uses a Script Include (extending `AbstractAjaxProcessor`) on the server to execute the code.”
- “Crucially, it avoids page reloads and offers a better user experience by using asynchronous calls (`getXML`).”
- “Explain how to pass parameters using `addParam()` and retrieve them with `this.getParameter()` in the Script Include.”
- “Mention the dangers of `getXMLWait()`.”
GlideRecord vs. GlideAjax: The Ultimate Showdown
Now that we’ve explored each API individually, let’s put them side-by-side to crystalize their differences and understand when to choose one over the other. This is often the core of an interviewer’s question or a critical decision point in your development.
Execution Context: Where the Action Happens
- GlideRecord: Exclusively server-side. It runs on the ServiceNow instance itself.
- GlideAjax: Initiated from the client-side (user’s browser) but executes its processing on the server-side via a Script Include. It’s the mechanism for client-to-server communication.
Purpose: What They’re Built For
- GlideRecord: Direct database interaction (CRUD operations). Its primary goal is to fetch, create, update, or delete records in ServiceNow tables. It’s about data manipulation.
- GlideAjax: To allow client-side scripts to request information or trigger server-side logic without a full page refresh. It’s about bringing server-side capabilities to the client-side experience.
Data Interaction: Direct vs. Indirect
- GlideRecord: Directly interacts with the database. Your script directly commands the database.
- GlideAjax: Indirectly interacts with the database. The client script asks the Script Include (via GlideAjax), and the Script Include then uses GlideRecord (or other server-side APIs) to interact with the database. GlideAjax itself doesn’t touch the database; it’s the messenger.
Synchronicity: Blocking vs. Non-Blocking
- GlideRecord: Typically synchronous within its execution context. When a `gr.query()` is called, the script waits for the database to respond before moving to the `while (gr.next())` loop. This is fine because it’s already on the server.
- GlideAjax: Primarily asynchronous (`getXML`, `getXMLAnswer`) to avoid freezing the client browser. Can be forced synchronous (`getXMLWait`) but this is heavily discouraged.
Security & Performance Considerations
- GlideRecord: Runs in a trusted server environment. Security is managed by ACLs and script logic. Performance depends on efficient queries and proper indexing. Poorly written GlideRecord queries can severely impact server performance.
- GlideAjax: While initiated on the client, the actual data processing happens on the server, benefiting from server-side security (ACLs apply to the GlideRecord calls within the Script Include). Performance can be affected by network latency between client and server, and also by inefficient server-side code in the Script Include.
When to Use Which: A Decision Guide
Here’s a quick mental flowchart to help you decide:
- Do you need to perform database operations (CRUD) directly on the server, without any user interface interaction?
- Example: A Business Rule that updates a related record after an incident is closed.
- Choose: GlideRecord
- Do you need to retrieve or validate data from the server, or execute complex server-side logic, as a user interacts with a form on the client-side, without refreshing the page?
- Example: Populating a field based on another field’s selection, validating user input against existing database records in real-time.
- Choose: GlideAjax (which will then likely use GlideRecord in its Script Include)
- Are you writing a Scheduled Job, Workflow Activity, or a Fix Script?
- These are all server-side contexts.
- Choose: GlideRecord
- Are you writing a Client Script or a UI Policy (that needs to fetch server data)?
- These are client-side contexts.
- Choose: GlideAjax
The Interviewer’s Favorite Question
“Explain the difference between GlideRecord and GlideAjax and when you would use each.” This is a classic, and now you’re equipped to answer it confidently. Emphasize the client vs. server context, the direct database interaction of GlideRecord versus the client-to-server communication of GlideAjax, and their respective primary purposes.
Troubleshooting Common Potholes
Even seasoned developers hit snags. Knowing how to diagnose issues with GlideRecord and GlideAjax can save hours of frustration.
GlideRecord Gotchas
- No Results? Double-check your query conditions. Misspelled field names or incorrect values are common culprits. Use `gs.info(gr.getEncodedQuery())` to see the actual query string being generated.
- Wrong Results? Are you missing `gr.query()` before your `while (gr.next())` loop? Or perhaps forgetting `gr.initialize()` for new records?
- `gs.log` is Your Friend: Litter your server-side scripts with `gs.info()` or `gs.debug()` statements to trace variable values and execution flow. Check the System Logs (`syslog.list`).
- ACLs: If your script runs fine as an admin but fails as a less privileged user, it’s likely an Access Control List issue preventing the script from reading or writing to certain fields/tables.
- Infinite Loops: Be careful with `update()` or `insert()` within `while (gr.next())` loops in Business Rules. If not conditioned correctly, it can trigger the same Business Rule repeatedly.
GlideAjax Headaches
- Client Script Not Firing: Check your client script’s conditions (UI Type, Table, Type, Field). Are you sure the `onChange`, `onLoad`, or `onSubmit` event is triggering?
- Script Include Not Called:
- Is the Script Include’s name in `new GlideAjax(‘Name’)` spelled exactly right?
- Is it marked ‘Client callable’?
- Does it extend `AbstractAjaxProcessor`?
- Is `sysparm_name` matching a public function in your Script Include? (Remember: functions starting with `_` are private).
- Empty Response:
- Check `gs.log()` in your Script Include – is it reaching the return statement?
- Are you returning `JSON.stringify(object)`? And parsing it correctly with `JSON.parse(answer)` on the client?
- In the network tab (browser developer tools), examine the XHR response from `xmlhttp.do` to see what the server actually sent back.
- Asynchronous Call Issues: Are you trying to use the server’s response before the callback function has even executed? Remember, code after `getXML()` runs immediately. Ensure dependent logic is inside your callback.
- Global vs. scoped apps: If you’re in a scoped application, your Script Include name might need the scope prefix (e.g., `x_scope_prefix.MyCustomAjaxUtils`).
Conclusion: Mastering Your ServiceNow Scripting Toolkit
GlideRecord and GlideAjax are two sides of the same coin in ServiceNow development, both indispensable but serving distinct purposes. GlideRecord is your powerhouse for direct, server-side database manipulation, the silent workhorse behind the scenes. GlideAjax is the nimble messenger, enabling dynamic, responsive user interfaces by bridging the client-side experience with server-side processing.
By understanding their fundamental differences in execution context, purpose, and interaction model, you not only write more effective and efficient code but also demonstrate a deeper comprehension of the ServiceNow platform’s architecture. Practice using both, troubleshoot actively, and you’ll soon wield these powerful APIs with confidence, transforming complex requirements into elegant solutions.
Happy scripting!