Skip to content

Developer track

Server-side scripting

GlideRecord, GlideAggregate, dates, and the habits that keep instances fast.

4 sections · 25 entries

GlideRecord & GlideAggregate

Generic, out-of-box tables only. Read before you run.

Query with a limit

Always limit when you only need a few rows. It is the cheapest performance win there is.

var gr = new GlideRecord('incident');
gr.addEncodedQuery('active=true^priority=1');
gr.orderByDesc('sys_created_on');
gr.setLimit(10);
gr.query();
while (gr.next()) {
  gs.info(gr.getValue('number') + ' · ' + gr.getDisplayValue('assigned_to'));
}

Get one record

get() returns true or false. Check it before using the record.

var gr = new GlideRecord('sys_user');
if (gr.get('user_name', 'abel.tuter')) {
  gs.info(gr.getValue('email'));
}

Insert a record

initialize() applies defaults; insert() returns the new sys_id (or null if it failed).

var inc = new GlideRecord('incident');
inc.initialize();
inc.setValue('short_description', 'Disk space low on app server');
inc.setValue('urgency', 2);
var newId = inc.insert();
gs.info('Created ' + inc.getValue('number') + ' (' + newId + ')');

Respect ACLsHidden gem

GlideRecordSecure enforces the current user’s ACLs. Use it in anything a user can trigger.

var gr = new GlideRecordSecure('incident');
gr.addActiveQuery();
gr.setLimit(5);
gr.query();
while (gr.next()) {
  gs.info(gr.getValue('number')); // only rows this user may read
}

Count rows (the right way)

GlideAggregate counts in the database. getRowCount() loads every row first.

var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.query();
var total = ga.next() ? parseInt(ga.getAggregate('COUNT'), 10) : 0;

Count by group

One query instead of one per group.

var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.groupBy('assignment_group');
ga.query();
while (ga.next()) {
  gs.info(ga.getDisplayValue('assignment_group') + ': ' + ga.getAggregate('COUNT'));
}

OR conditions in code

addOrCondition() hangs off the condition it belongs to.

var gr = new GlideRecord('incident');
var q = gr.addQuery('priority', 1);
q.addOrCondition('priority', 2);
gr.addNullQuery('assigned_to'); // AND unassigned
gr.query();

Bulk update, quietly

setWorkflow(false) skips business rules and notifications. Only use it when you mean to.

var gr = new GlideRecord('incident');
gr.addEncodedQuery('state=6^resolved_at<javascript:gs.daysAgoStart(30)');
gr.setLimit(500); // run in batches
gr.query();
while (gr.next()) {
  gr.setValue('state', 7);
  gr.setWorkflow(false);
  gr.autoSysFields(false); // keep sys_updated_on / sys_updated_by as they were
  gr.update();
}

updateMultiple / deleteMultipleHidden gem

Set-based and fast, but no per-row logic. Always test the query with a count first.

var gr = new GlideRecord('u_staging_temp');
gr.addEncodedQuery('sys_created_on<javascript:gs.daysAgoStart(90)');
gr.deleteMultiple();

var up = new GlideRecord('incident');
up.addEncodedQuery('active=true^assignment_groupISEMPTY');
up.setValue('assignment_group', gs.getProperty('x_rsa.default_group'));
up.updateMultiple();

Dates, users & system

GlideDateTime and GlideSystem calls you will reach for weekly.

GlideDateTime basics

Store and compare in UTC; show in the user’s time zone with getDisplayValue().

var now = new GlideDateTime();             // now, UTC
var due = new GlideDateTime();
due.addDaysUTC(3);                           // +3 days
gs.info(due.getDisplayValue());              // user's time zone
gs.info(due.after(now));                     // true
var diff = GlideDateTime.subtract(now, due); // GlideDuration
gs.info(diff.getDisplayValue());             // "3 Days"

GlideSystem essentials

Read config from properties, never hardcode it.

gs.getUserID();                     // current user's sys_id
gs.getUserName();                   // user_name
gs.hasRole('itil');                 // true / false
gs.getProperty('x_rsa.api.timeout', '30');
gs.nil(value);                      // null, undefined or ''
gs.addInfoMessage('Saved.');        // banner on the next page
gs.eventQueue('x_rsa.thing.done', current, 'parm1', 'parm2');

Dot-walk and reference recordsHidden gem

getRefRecord() gives you a full GlideRecord for a reference field.

var caller = current.caller_id.getRefRecord();
if (caller.isValidRecord()) {
  gs.info(caller.getValue('email'));
}
gs.info(current.caller_id.manager.getDisplayValue()); // dot-walk two levels

Business rule timing

Pick the right "When" and half your bugs disappear.

WhenUse it forAvoid
beforeSet or validate fields on the same record
afterUpdate other records once this one is saved
asyncSlow work: REST calls, heavy queries
displayFill g_scratchpad for client scripts
before + Query boxSilently restrict which rows users can see

Good habits

The rules senior developers follow without thinking.

  • ✦getValue(). Prefer getValue() and setValue() over dot access: you get strings, not GlideElement objects.
  • ✦No current.update(). Never call current.update() in a before business rule. The record saves anyway.
  • ✦Script Includes. Put reusable logic in Script Includes, not in business rules or UI actions.
  • ✦No sys_ids. No hardcoded sys_ids. Use system properties so code works in every instance.
  • ✦Dry run. Test background scripts with setLimit() and gs.info() before running them for real.
  • ✦Update sets. Pick the named update set (or app scope) before you start, not after.
  • ✦previous. In business rules, compare current with previous or use changes() / changesTo().
  • ✦Scoped apps. Build new work in a scoped app: cleaner upgrades, clearer ownership.

Snippets are provided as-is under the MIT licence, without warranty. Test in a sub-production instance first. ServiceNow is a trademark of ServiceNow, Inc.; this page is independent and not affiliated with or endorsed by ServiceNow.