1. The Class-Based Script Include Pattern
ServiceNow's server-side JavaScript predates ES6 classes, so Script Includes use
a prototype-based pattern instead — but the shape is exactly what you'd expect
from any other object-oriented service: a constructor-style function, methods on
its prototype, and a type string matching the record's
name.
var IncidentUtils = Class.create();
IncidentUtils.prototype = {
initialize: function() {},
countOpenByGroup: function(groupSysId) {
var ga = new GlideAggregate('incident');
ga.addQuery('assignment_group', groupSysId);
ga.addActiveQuery();
ga.addAggregate('COUNT');
ga.query();
return ga.next() ? ga.getAggregate('COUNT') : 0;
},
type: 'IncidentUtils'
};
Every field in the object literal after initialize becomes a
method callable on an instance. This is what a Business Rule or another Script
Include calls into — never write GlideRecord logic directly in three different
Business Rules when it can live once, here, and get called from all three.
2. Calling a Script Include
var utils = new IncidentUtils();
var count = utils.countOpenByGroup(current.assignment_group);
if (count > 20) {
current.priority = 1;
}
Script Includes load lazily by name — you don't need an import or
a manual reference; referencing the class name anywhere in server-side script is
enough for the platform to locate and load the matching Script Include record.
3. Client-Callable Script Includes
By default, a Script Include is server-only — it can't be
invoked from browser-side JavaScript at all, which is the safe default (server
logic shouldn't be casually exposed to the client). To allow a client script to
call in via GlideAjax (Week 8), two things must both be true: the Script
Include's Client callable checkbox is checked, and it extends
AbstractAjaxProcessor.
var IncidentAjax = Class.create();
IncidentAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getOpenCountForGroup: function() {
var groupSysId = this.getParameter('sysparm_group');
var utils = new IncidentUtils();
return utils.countOpenByGroup(groupSysId);
},
type: 'IncidentAjax'
});
this.getParameter(name) reads a value the client sent via
GlideAjax.addParam() — you'll use this pairing directly next week
once ACLs are in place to control who's even allowed to call it, and the week
after that when you wire up the browser side.
Every method on a client-callable Script Include is reachable from any authenticated (or even anonymous, depending on ACLs) browser session — treat it like a small API endpoint, not an internal implementation detail, and validate parameters defensively inside it.
4. Hands-on Exercise
Extract duplicated logic into a Script Include
Requirements:
- Create
IncidentUtilsas shown, plus a second methodisOverdue(incidentSysId)that returns true if a P1/P2 incident has been open more than 4 hours. - Call
isOverdue()from a Business Rule that sets a customu_escalation_flagboolean field when true. - Create
IncidentAjaxextendingAbstractAjaxProcessor, with a method exposingcountOpenByGroupto the client (don't wire up the browser call yet — that's Week 8). - Confirm in the Script Include's record that "Client callable" is checked only on
IncidentAjax, not onIncidentUtils.
5. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What's the Angular-service analogy for a Script Include, and where does it break down?
What's the Angular-service analogy for a Script Include, and where does it break down?
Both are reusable, injectable-feeling units of logic that other code calls into rather than duplicating. It breaks down in mechanics: Script Includes use ES5 prototype-based classes (Class.create()) rather than TypeScript classes with decorators, and there's no dependency-injection container — you reference the class name directly and the platform lazily locates the matching record.
Q2
Why is a Script Include server-only by default?
Why is a Script Include server-only by default?
Exposing server-side logic to the browser is a deliberate security decision, not a default — most server logic (data access, business logic) has no reason to be callable from arbitrary client-side code. Requiring both the "Client callable" checkbox and extending AbstractAjaxProcessor makes client-exposure an explicit, visible opt-in rather than something that happens by accident.
Q3
What two things must be true for a Script Include to be callable via GlideAjax from the browser?
What two things must be true for a Script Include to be callable via GlideAjax from the browser?
Its "Client callable" checkbox must be checked, and its prototype must extend AbstractAjaxProcessor (via Object.extendsObject). Missing either one means a GlideAjax call to it will fail even if the method itself is written correctly.
Q4
Why should a client-callable Script Include validate its parameters defensively?
Why should a client-callable Script Include validate its parameters defensively?
Every method on it is reachable from any browser session that can reach the instance — effectively a small API surface, not a private implementation detail. Parameters arriving via getParameter() come from client-side script that a user could tamper with, so the same defensive validation you'd apply to any externally-reachable endpoint applies here.