Why GlideAjax exists
Client Scripts run in the browser. They cannot directly access the ServiceNow database, query GlideRecord, read system properties, or call server-side APIs. Traditionally, developers used synchronous GlideRecord on the client side — but that is deprecated because it blocks the browser's main thread, freezing the UI until the server responds.
GlideAjax provides the asynchronous alternative. The client sends a request to a named server-side Script Include, the server processes it, and the result is delivered to a callback function — without blocking the UI and without a full page reload.
The two parts of every GlideAjax call
Every GlideAjax implementation has exactly two parts:
- A server-side Script Include that extends AbstractAjaxProcessor and has the Client callable checkbox checked
- A client-side call using the GlideAjax API that references the Script Include by name and calls a method on it
Step 1 — Building the server-side Script Include
Three requirements make a Script Include client-callable:
- The Script Include record must have Client callable checked
- The class must extend AbstractAjaxProcessor
- Methods must use this.getParameter() to read client-supplied values
var IncidentAjaxUtils = Class.create();
IncidentAjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
// Method 1 — returns a simple string
getOpenCountForCaller: function() {
var callerId = this.getParameter('sysparm_caller_id');
if (!callerId) return '0';
var ga = new GlideAggregate('incident');
ga.addEncodedQuery('active=true^caller_id=' + callerId);
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
return ga.getAggregate('COUNT');
}
return '0';
},
// Method 2 — returns complex data as JSON
getIncidentSummary: function() {
var sysId = this.getParameter('sysparm_sys_id');
var result = { found: false };
var gr = new GlideRecord('incident');
if (gr.get(sysId)) {
result.found = true;
result.number = gr.getValue('number');
result.state = gr.getDisplayValue('state');
result.priority = gr.getDisplayValue('priority');
result.assignedTo = gr.getDisplayValue('assigned_to');
result.shortDesc = gr.getValue('short_description');
result.openDays = calculateDaysSince(gr.getValue('opened_at'));
}
return JSON.stringify(result);
},
// Method 3 — lookup by field value
getUserDepartment: function() {
var userId = this.getParameter('sysparm_user_id');
var gr = new GlideRecord('sys_user');
if (gr.get(userId)) {
return gr.getDisplayValue('department');
}
return '';
},
type: 'IncidentAjaxUtils'
});
function calculateDaysSince(dateStr) {
if (!dateStr) return 0;
var opened = new GlideDateTime(dateStr);
var now = new GlideDateTime();
return Math.floor(GlideDateTime.subtract(opened, now).getDayPart());
}
The getParameter() naming convention
All parameters sent from the client and received by the server must start with sysparm_. This is enforced by the platform — parameters without this prefix are ignored.
// Server side reads with:
this.getParameter('sysparm_my_value');
// Client side sends with:
ga.addParam('sysparm_my_value', someValue);
The name sysparm_name is reserved — it tells the server which method to call. Never use it for your own parameters.
Step 2 — Calling from a Client Script
// In an onChange Client Script
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue) return;
// Create the GlideAjax call
var ga = new GlideAjax('IncidentAjaxUtils'); // Script Include name
ga.addParam('sysparm_name', 'getOpenCountForCaller'); // Method to call
ga.addParam('sysparm_caller_id', newValue); // Custom parameter
// Always async — pass a callback function
ga.getXMLAnswer(function(answer) {
// answer is the string returned by the server method
var count = parseInt(answer);
if (count > 5) {
g_form.showFieldMsg('caller_id',
'This caller has ' + count + ' open incidents',
'warning');
} else {
g_form.hideFieldMsg('caller_id');
}
});
}
Calling a JSON-returning method
function onLoad() {
var ga = new GlideAjax('IncidentAjaxUtils');
ga.addParam('sysparm_name', 'getIncidentSummary');
ga.addParam('sysparm_sys_id', g_form.getUniqueValue());
ga.getXMLAnswer(function(answer) {
try {
var data = JSON.parse(answer);
if (data.found) {
// Use the rich data from the server
g_form.showFieldMsg('number',
'Open for ' + data.openDays + ' days — Priority: ' + data.priority,
'info');
}
} catch (e) {
console.log('GlideAjax JSON parse error: ' + e);
}
});
}
getXMLAnswer vs getXML — always use getXMLAnswer
// WRONG — synchronous, blocks the browser, deprecated
var ga = new GlideAjax('MyScriptInclude');
ga.addParam('sysparm_name', 'myMethod');
ga.getXML(); // Never use this — freezes the browser
// RIGHT — asynchronous with callback
ga.getXMLAnswer(function(answer) {
// handle response here
});
// Also available — getXML with callback (legacy pattern)
ga.getXML(myCallbackFunction);
function myCallbackFunction(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
// process answer
}
// But getXMLAnswer is simpler and preferred
Checking the answer before using it
ga.getXMLAnswer(function(answer) {
// Always validate before using
if (!answer || answer === 'null' || answer === 'undefined') {
console.log('GlideAjax returned empty/null');
return;
}
// For JSON responses, wrap parse in try/catch
try {
var data = JSON.parse(answer);
processData(data);
} catch (e) {
console.log('Parse error: ' + e + ' | Raw: ' + answer);
}
});
Making multiple GlideAjax calls efficiently
If you need multiple pieces of server data, combine them into one GlideAjax call rather than making multiple separate calls. Design the server method to return all needed data as a single JSON object:
// Bad — two separate server round trips
var ga1 = new GlideAjax('UserUtils');
ga1.addParam('sysparm_name', 'getDepartment');
ga1.addParam('sysparm_user_id', userId);
ga1.getXMLAnswer(function(dept) { g_form.setValue('department', dept); });
var ga2 = new GlideAjax('UserUtils');
ga2.addParam('sysparm_name', 'getManager');
ga2.addParam('sysparm_user_id', userId);
ga2.getXMLAnswer(function(mgr) { g_form.setValue('manager', mgr); });
// Good — one round trip, all data returned as JSON
var ga = new GlideAjax('UserUtils');
ga.addParam('sysparm_name', 'getUserContext');
ga.addParam('sysparm_user_id', userId);
ga.getXMLAnswer(function(answer) {
var data = JSON.parse(answer);
g_form.setValue('department', data.department);
g_form.setValue('manager', data.managerId, data.managerName);
g_form.setValue('location', data.location);
});
GlideAjax vs Display Business Rules
For data you know you will need on every form load, a Display Business Rule with g_scratchpad is more efficient than GlideAjax. Display rules pre-load data before the form renders — no extra request after load. Use GlideAjax for dynamic responses to user interactions (onChange, onSubmit checks) and for conditional data fetching. See our guide on the setValue vs setDisplayValue distinction.
Related guides:
- Script Includes guide — building the server-side AbstractAjaxProcessor classes
- Client Script types — where GlideAjax calls originate
- Business Rule types — Display rules as an alternative for form-load data
- Debugging scripts — debugging GlideAjax server and client issues
- GlideAjax examples — more production patterns