ServiceNow
Every Scripting Keyword That Matters in ServiceNow: current, previous, g_form, gs, and the Objects You Must Know
How ServiceNow injects objects into your scripts
When you write a Business Rule, Client Script, or Script Include, ServiceNow wraps your code and injects specific objects depending on the context. You do not import or declare these — they just exist.
current (server-side)
Available in: Business Rules, Workflow scripts, Flow Designer script steps
current is a GlideRecord object pointing to the record being operated on. When a user saves an incident, current is that incident.
// Before Business Rule on incident table
(function executeRule(current, previous) {
gs.info(current.getValue('number')); // INC0010001
gs.info(current.getValue('state')); // 1
gs.info(current.getTableName()); // incident
gs.info(current.isNewRecord()); // true on insert, false on update
gs.info(current.isActionAborted()); // false (unless someone called setAbortAction)
// Modify the record before it saves
current.priority = 1;
current.work_notes = 'Auto-escalated by Business Rule';
// Abort the save entirely
// current.setAbortAction(true);
// gs.addErrorMessage('Cannot save: missing required data');
})(current, previous);
When current is null
- Script Includes called directly (not from a BR context) —
currentis not defined - Background Scripts —
currentis not defined unless you create your own GlideRecord - Scheduled Jobs —
currentis not defined
previous (server-side)
Available in: Business Rules (update and delete operations only)
previous is a read-only GlideRecord snapshot of the record BEFORE the current changes. It lets you compare old vs new values.
(function executeRule(current, previous) {
// Check if priority changed
if (current.getValue('priority') != previous.getValue('priority')) {
gs.info('Priority changed from ' + previous.getValue('priority') +
' to ' + current.getValue('priority'));
}
// Check if the record was reassigned
if (current.getValue('assigned_to') != previous.getValue('assigned_to')) {
gs.info('Reassigned from ' + previous.getDisplayValue('assigned_to') +
' to ' + current.getDisplayValue('assigned_to'));
}
})(current, previous);
When previous is null
- On insert:
previousis null — there is no "before" state for a new record. Always check:if (previous)before using it. - In async Business Rules:
previousis NOT available. Async BRs run outside the original transaction, so the previous state is gone. - In Display Business Rules:
previousis not available (the record is being read, not modified).
// Safe pattern — always guard against null previous
(function executeRule(current, previous) {
if (previous && current.state.changesTo(6)) {
// State changed to Resolved — only runs on update, not insert
gs.eventQueue('incident.resolved', current, gs.getUserID(), '');
}
})(current, previous);
GlideElement methods on current/previous
When you access current.priority, you do not get a number or string. You get a GlideElement object with these methods:
// changes() — did this field change in this transaction?
if (current.priority.changes()) {
gs.info('Priority was modified');
}
// changesTo(value) — did it change TO this specific value?
if (current.state.changesTo(6)) {
gs.info('State just changed to Resolved');
}
// NOTE: the value must be the INTERNAL value (number), not the display label.
// changesTo('Resolved') will NOT work. Use changesTo(6).
// changesFrom(value) — did it change FROM this specific value?
if (current.state.changesFrom(1)) {
gs.info('Was previously New, now something else');
}
// nil() — is the field empty/null?
if (current.assigned_to.nil()) {
gs.info('Nobody assigned');
}
// toString() — convert GlideElement to string
var desc = current.short_description.toString();
// getDisplayValue() — the human-readable label
gs.info(current.state.getDisplayValue()); // 'New', 'In Progress', etc.
// getReferenceTable() — for reference fields, get the target table
gs.info(current.caller_id.getReferenceTable()); // 'sys_user'
// getRefRecord() — get the full referenced record
var callerGR = current.caller_id.getRefRecord();
gs.info(callerGR.getValue('email')); // caller's email address
gs (GlideSystem) — the utility belt
Available in: all server-side scripts. gs is a global object with system utility methods.
User context
gs.getUserID(); // sys_id of logged-in user
gs.getUserName(); // 'john.smith'
gs.getUserDisplayName(); // 'John Smith'
gs.hasRole('admin'); // true/false
gs.hasRole('itil'); // true/false
// Get the full user GlideRecord
var userGR = gs.getUser().getRecord();
gs.info(userGR.getValue('email'));
Messages
// These show banners on the user's screen (only in before/after BRs)
gs.addInfoMessage('Record updated successfully.');
gs.addErrorMessage('Cannot save: priority 1 requires an assignment group.');
// These do NOT work in:
// - Async Business Rules (user's transaction is already done)
// - Scheduled Jobs (no user session)
// - Script Includes called from REST APIs
Dates and time
gs.now(); // '2026-04-07 14:30:00' (user's timezone) gs.nowDateTime(); // '2026-04-07 14:30:00' (system format) gs.daysAgo(7); // date/time 7 days ago gs.daysAgoStart(7); // start of day, 7 days ago (midnight) gs.beginningOfLastMonth(); // first day of previous month gs.endOfLastMonth(); // last day of previous month
Checking for empty values
// gs.nil() is the safest way to check for empty values
gs.nil(null); // true
gs.nil(undefined); // true
gs.nil(''); // true
gs.nil(0); // false (zero is not nil)
gs.nil('hello'); // false
// Use it to guard against empty fields
var assignee = gr.getValue('assigned_to');
if (gs.nil(assignee)) {
gs.info('No assignee');
}
g_form (client-side)
Available in: Client Scripts, UI Actions (client-side). NOT available on the server.
g_form controls the form the user is looking at in their browser.
// onChange Client Script
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return; // do not run when the form first loads
// Read a field value
var priority = g_form.getValue('priority'); // '1', '2', '3', etc.
// Set a field value
g_form.setValue('urgency', '1');
// Make a field mandatory
g_form.setMandatory('assignment_group', true);
// Hide a field
g_form.setVisible('u_internal_only', false);
// Show a message
g_form.addInfoMessage('Priority updated.');
}
// onLoad Client Script — runs when the form opens
function onLoad() {
if (g_form.isNewRecord()) {
g_form.setValue('contact_type', 'self-service');
}
}
// onSubmit Client Script — runs when the user clicks Save
function onSubmit() {
var desc = g_form.getValue('short_description');
if (desc.length < 10) {
g_form.addErrorMessage('Description must be at least 10 characters.');
return false; // prevents the form from submitting
}
return true;
}
g_user (client-side)
// Available in Client Scripts
g_user.userName; // 'john.smith'
g_user.userID; // sys_id of current user
g_user.getFullName(); // 'John Smith'
g_user.hasRole('itil'); // true/false
g_user.hasRoleExactly('incident_manager'); // true only for that specific role
// Common pattern: show/hide fields based on role
function onLoad() {
if (!g_user.hasRole('itil')) {
g_form.setVisible('u_technical_notes', false);
}
}
The client-server boundary
This is the single most important concept in ServiceNow scripting. The server and client are completely separate environments. You cannot call GlideRecord from a Client Script. You cannot call g_form from a Business Rule.
Getting server data from the client: GlideAjax
// Step 1: Create a Script Include on the server
// Name: IncidentUtils
// Must extend AbstractAjaxProcessor to be callable from client
var IncidentUtils = Class.create();
IncidentUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getCallerEmail: function() {
var userId = this.getParameter('sysparm_user_id');
var gr = new GlideRecord('sys_user');
if (gr.get(userId)) {
return gr.getValue('email');
}
return '';
},
type: 'IncidentUtils'
});
// Step 2: Call it from a Client Script
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue) return;
var ga = new GlideAjax('IncidentUtils');
ga.addParam('sysparm_name', 'getCallerEmail');
ga.addParam('sysparm_user_id', newValue);
ga.getXMLAnswer(function(answer) {
// 'answer' is the string returned by the server method
g_form.setValue('u_caller_email', answer);
});
}
// KEY RULES:
// 1. Script Include MUST extend AbstractAjaxProcessor
// 2. Use this.getParameter() to read params on the server
// 3. Always use getXMLAnswer with a callback (async)
// 4. Return values are always strings
Pushing server data to the client: g_scratchpad
// Display Business Rule (server-side, runs on form load)
(function executeRule(current, previous) {
g_scratchpad.isVIP = String(current.caller_id.vip == true);
g_scratchpad.openCount = '42'; // must be string/simple types
})(current, previous);
// Client Script (onLoad) — reads g_scratchpad
function onLoad() {
if (g_scratchpad.isVIP === 'true') {
g_form.addInfoMessage('This caller is a VIP.');
}
}
// g_scratchpad avoids a GlideAjax round-trip.
// Use it for data you know you will need on every form load.
Putting it all together: a real example
// GOAL: When a user sets priority to 1 on an incident form,
// make assignment_group mandatory, show a warning,
// and auto-populate the escalation contact from the server.
// === Before Business Rule (server) ===
// Table: incident, When: before, Insert/Update: true
(function executeRule(current, previous) {
if (current.getValue('priority') == '1' && current.assigned_to.nil()) {
// Server-side validation — catches API and import submissions too
current.setAbortAction(true);
gs.addErrorMessage('P1 incidents must have an assigned person.');
}
})(current, previous);
// === Client Script (onChange on priority field) ===
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
if (newValue == '1') {
g_form.setMandatory('assignment_group', true);
g_form.addInfoMessage('P1 selected — assignment group is now required.');
// Fetch escalation contact from server
var ga = new GlideAjax('IncidentUtils');
ga.addParam('sysparm_name', 'getEscalationContact');
ga.addParam('sysparm_group', g_form.getValue('assignment_group'));
ga.getXMLAnswer(function(contact) {
if (contact) {
g_form.setValue('u_escalation_contact', contact);
}
});
} else {
g_form.setMandatory('assignment_group', false);
}
}
// This covers both sides: the client makes the field mandatory
// and shows a message, while the server enforces it for API callers
// who bypass the form entirely.