GlideAjax is how Client Scripts communicate with the server in ServiceNow. It allows you to run server-side code (GlideRecord queries, Script Include methods, system property reads) from a client-side script without refreshing the page. This guide covers how it actually works and the patterns used in production.
Why GlideAjax Exists
Client Scripts run in the browser — they cannot directly access the database, query GlideRecord, or call server-side APIs. GlideAjax bridges that gap by making an asynchronous HTTP request to a server-side Script Include and returning the result to the client callback.
The Two Parts — Script Include and Client Script
Every GlideAjax implementation has two parts:
- Server side — a Script Include that extends
AbstractAjaxProcessor
- Client side — a Client Script that calls the Script Include using GlideAjax
Server Side — Building the Script Include
// Script Include: UserUtils
// Client callable: true (must be checked)
var UserUtils = Class.create();
UserUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
// Method called by the client
getUserManager: function() {
// Read parameters sent from client
var userSysId = this.getParameter('sysparm_user_id');
// Do server-side work
var gr = new GlideRecord('sys_user');
if (gr.get(userSysId)) {
// Return data to client
return gr.getDisplayValue('manager');
}
return '';
},
// Another method on the same Script Include
getDepartmentUsers: function() {
var dept = this.getParameter('sysparm_dept');
var gr = new GlideRecord('sys_user');
gr.addQuery('department.name', dept);
gr.addQuery('active', true);
gr.query();
var users = [];
while (gr.next()) {
users.push({
sys_id: gr.getUniqueValue(),
name: gr.getDisplayValue('name')
});
}
// Return JSON string for complex data
return JSON.stringify(users);
},
type: 'UserUtils'
});
Client Side — Calling the Script Include
// Client Script — onChange on assigned_to field
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue) return;
// Create GlideAjax object with Script Include name
var ga = new GlideAjax('UserUtils');
// Specify which method to call
ga.addParam('sysparm_name', 'getUserManager');
// Pass parameters to the server method
ga.addParam('sysparm_user_id', newValue);
// Make the async call — callback fires when server responds
ga.getXMLAnswer(function(answer) {
// 'answer' is the string returned by the server method
g_form.setValue('u_manager', answer);
g_form.setDisplay('u_manager', answer !== '');
});
}
Returning Complex Data — JSON Pattern
// Client Script calling getDepartmentUsers
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue) return;
var ga = new GlideAjax('UserUtils');
ga.addParam('sysparm_name', 'getDepartmentUsers');
ga.addParam('sysparm_dept', g_form.getDisplayValue('department'));
ga.getXMLAnswer(function(answer) {
// Parse the JSON string returned by the server
var users = JSON.parse(answer);
// Use the data — e.g., populate a reference field options
users.forEach(function(user) {
console.log(user.sys_id + ': ' + user.name);
});
});
}
getXMLAnswer vs getXML
| Method | Use when |
getXMLAnswer(callback) | Server returns a single value via return |
getXML(callback) | Server uses this.newItem() to return structured XML |
For most use cases, getXMLAnswer() with a JSON string return is the cleanest approach.
Critical: Client Callable Must Be Checked
The Script Include must have Client callable checked, otherwise GlideAjax calls will fail with an access error. This is the most common reason GlideAjax calls appear to do nothing — the Script Include is not marked as client callable.
Error Handling
// Server side — return structured response
getUserManager: function() {
var userSysId = this.getParameter('sysparm_user_id');
if (!userSysId) {
return JSON.stringify({ error: 'user_id required', data: null });
}
var gr = new GlideRecord('sys_user');
if (!gr.get(userSysId)) {
return JSON.stringify({ error: 'user not found', data: null });
}
return JSON.stringify({
error: null,
data: gr.getDisplayValue('manager')
});
},
// Client side — handle errors
ga.getXMLAnswer(function(answer) {
var result = JSON.parse(answer);
if (result.error) {
console.error('GlideAjax error: ' + result.error);
return;
}
g_form.setValue('u_manager', result.data);
});
Performance Consideration
GlideAjax calls are asynchronous — they do not block the UI. However, every call is an HTTP round trip to the server. Avoid making multiple GlideAjax calls in sequence — design your Script Include to return all the data needed in a single call.
// ❌ Two separate server calls
ga1.getXMLAnswer(function(manager) {
ga2.getXMLAnswer(function(department) {
// Two round trips
});
});
// ✅ One call returning all needed data
getUserDetails: function() {
var gr = new GlideRecord('sys_user');
gr.get(this.getParameter('sysparm_user_id'));
return JSON.stringify({
manager: gr.getDisplayValue('manager'),
department: gr.getDisplayValue('department'),
location: gr.getDisplayValue('location')
});
}
Passing multiple parameters from client to server
// Client Script — passing multiple values
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue) return;
var ga = new GlideAjax('LocationUtils');
ga.addParam('sysparm_name', 'getBuildingInfo');
ga.addParam('sysparm_location_id', newValue);
ga.addParam('sysparm_include_floors', 'true');
ga.addParam('sysparm_format', 'full');
ga.getXMLAnswer(function(answer) {
if (!answer) return;
try {
var data = JSON.parse(answer);
g_form.setValue('u_building_name', data.building);
g_form.setValue('u_building_manager', data.manager_id, data.manager_name);
if (data.floors) {
populateFloorDropdown(data.floors);
}
} catch(e) {
console.error('GlideAjax parse error: ' + e);
}
});
}
// Script Include — receiving multiple parameters
getBuildingInfo: function() {
var locationId = this.getParameter('sysparm_location_id');
var includeFloors = this.getParameter('sysparm_include_floors') === 'true';
var result = { found: false };
var gr = new GlideRecord('cmn_location');
if (gr.get(locationId)) {
result.found = true;
result.building = gr.getValue('name');
result.manager_id = gr.getValue('manager');
result.manager_name = gr.getDisplayValue('manager');
if (includeFloors) {
result.floors = [];
var fr = new GlideRecord('cmn_building');
fr.addQuery('location', locationId);
fr.query();
while (fr.next()) {
result.floors.push({
value: fr.getUniqueValue(),
label: fr.getValue('name')
});
}
}
}
return JSON.stringify(result);
}
Error handling on the server side
validateAndLookup: function() {
var sys_id = this.getParameter('sysparm_sys_id');
// Validate input before querying
if (!sys_id || sys_id.length !== 32) {
return JSON.stringify({ error: 'Invalid sys_id', found: false });
}
try {
var gr = new GlideRecord('incident');
if (!gr.get(sys_id)) {
return JSON.stringify({ error: 'Record not found', found: false });
}
return JSON.stringify({
found: true,
number: gr.getValue('number'),
state: gr.getDisplayValue('state')
});
} catch(e) {
gs.error('GlideAjax validateAndLookup error: ' + e.getMessage(), 'AjaxUtils');
return JSON.stringify({ error: 'Server error', found: false });
}
}
GlideAjax on catalog item forms
GlideAjax works on Service Catalog item variable forms, not just standard forms. The API is identical — the catalog Client Script runs in the browser just like a regular Client Script:
// Catalog Client Script — onCatalogItemValueChange or similar
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
var ga = new GlideAjax('ProductUtils');
ga.addParam('sysparm_name', 'getPriceForSKU');
ga.addParam('sysparm_sku', newValue);
ga.getXMLAnswer(function(answer) {
var price = parseFloat(answer);
if (!isNaN(price)) {
g_form.setValue('price', price.toFixed(2));
}
});
}
When to use g_scratchpad instead of GlideAjax
For data you know you will need on every form load — regardless of user interaction — a Display Business Rule with g_scratchpad is more efficient. The Display rule runs server-side before the form renders, populating g_scratchpad with data that is then available in onLoad Client Scripts without any additional server round trip. Use GlideAjax for data that is conditionally needed based on user actions (onChange events, user clicking something), and g_scratchpad for data needed unconditionally on form load.
// Display Business Rule (server-side, runs before form loads)
g_scratchpad.userDepartment = current.caller_id.department.getDisplayValue();
g_scratchpad.openIncidentCount = getOpenCountForCaller(current.caller_id.toString());
// onLoad Client Script (reads from scratchpad — no round trip)
function onLoad() {
var dept = g_scratchpad.userDepartment;
var count = g_scratchpad.openIncidentCount;
if (count > 5) {
g_form.showFieldMsg('caller_id', 'This caller has ' + count + ' open incidents', 'info');
}
}
Related: GlideAjax complete guide · Script Includes · Client Script types · Business Rules · Debugging scripts
Security considerations for GlideAjax Script Includes
Client-callable Script Includes are exposed to browser-side requests. Any user who can access the ServiceNow instance can call them. Key security practices:
- Never return data the caller should not see — the Script Include runs as the server-side user context (usually the logged-in user, but verify). ACLs on the tables you query still apply, but if your script accepts a sys_id parameter and returns record data, make sure the calling user has ACL read access to that record before returning it.
- Validate all parameters — never trust client-supplied sys_ids or field values without validation. Check they are valid GUIDs, check the length, check against expected values.
- Do not expose sensitive data — a Script Include that returns user salary data or security credentials is a data exposure risk even if the Script Include itself is correctly scoped.
// Safe pattern — validate, check ACL, then return
getUserSensitiveInfo: function() {
var targetUserId = this.getParameter('sysparm_user_id');
// Only allow users to look up their own info, or admins to look up any user
if (targetUserId !== gs.getUserID() && !gs.hasRole('admin')) {
return JSON.stringify({ error: 'Access denied', found: false });
}
var gr = new GlideRecord('sys_user');
if (gr.get(targetUserId)) {
return JSON.stringify({
found: true,
name: gr.getValue('name'),
department: gr.getDisplayValue('department')
// Do NOT include salary, SSN, or other sensitive fields
});
}
return JSON.stringify({ found: false });
}
Debugging GlideAjax calls
GlideAjax issues typically fall into two categories: the server method is not being reached, or it is being reached but returning unexpected data. Debugging approach:
- In the browser console, add
console.log('GlideAjax answer:', answer) inside the callback to see the raw server response before parsing
- Check that the Script Include has Client callable checked on the record
- Verify the method name passed as
sysparm_name exactly matches the method name in the Script Include (case-sensitive)
- Add
gs.log() calls inside the Script Include method — they appear in the System Log (System > System Log > All)
- Use the Session Debug tools to trace Script Include execution
GlideAjax as a career differentiator
Mastering GlideAjax clearly separates administrators from developers on the ServiceNow platform. Administrators configure — they work with forms, lists, workflows, catalog items. Developers build — they construct the client/server interactions that make forms dynamic, responsive, and intelligent. GlideAjax is the bridge. Building working end-to-end GlideAjax examples on your Personal Developer Instance is one of the most effective demonstrations you can bring to a technical interview — a single working example covers Client Scripts, Script Includes, AbstractAjaxProcessor, asynchronous execution, and JSON data transfer, showing breadth across all the areas the CAD exam tests. If you are on the admin-to-developer transition path, build at least three GlideAjax examples that solve real problems — validation against related records, dynamic field population, data lookup on change — and be able to walk through every line of both the server and client sides in an interview context.
Troubleshooting the most common GlideAjax failure modes
Three failure modes account for 90% of GlideAjax issues developers encounter: the Script Include is not client-callable, the method name in sysparm_name has a typo or case mismatch, or the server method is throwing an exception that causes it to return null rather than the expected JSON. The debugging process for all three starts the same way — console.log('raw answer:', answer) in the callback, then check the System Log for server-side errors. If answer is null or undefined, the server method did not execute or returned nothing. If answer is an HTML error page, the Script Include record has a syntax error. If answer is a valid but empty string, the method executed but returned nothing explicitly. See Debugging ServiceNow scripts for the full diagnostic toolkit.
Want the complete reference?
This article is part of the NowSpectrum knowledge library. Browse all products for cheat sheets, interview prep, and deep-dive reference guides.
Browse All Products →
Free Weekly Newsletter
One practical ServiceNow tip every week.
Written by working professionals. No fluff. Free forever.
Subscribe Free →