Minimize Server Lookups in Client Scripts

Use g_scratchpad and GlideAjax on purpose

Client scripts get slow and noisy when every small decision turns into another server call.

Most of the time, the real problem is not the API. It is choosing the wrong moment to fetch data.

When I write client-side logic, I usually ask three questions in this order:

  1. Is the data already on the client?
  2. If not, can I send it with the initial form load?
  3. If not, do I need an on-demand lookup?

That decision usually leads to one of two patterns: g_scratchpad or asynchronous GlideAjax.

1. Use data that is already on the client first

Before calling the server, check whether the form already has what you need.

If a value is already on the form, read it with g_form.getValue() or g_form.getDisplayValue(). If the logic depends on the current user, g_user may already be enough. A surprising amount of client code can stay fully client-side.

Bad:

var ga = new GlideAjax("UserUtils");
ga.addParam("sysparm_name", "isVipCaller");
ga.addParam("sysparm_user", g_form.getValue("caller_id"));
ga.getXML(handleResponse);

Better if the form already contains the needed field:

var vip = g_form.getValue("u_vip");
if (vip == "true") {
  g_form.showFieldMsg("caller_id", "VIP caller", "info");
}

The fastest lookup is the one you do not make.

2. Use g_scratchpad for data known at form load

If the client needs server-side information as soon as the form opens, g_scratchpad is usually the cleanest option.

This works well when the value can be prepared before the form is shown. Typical examples are:

  • whether the current user has a special role for this record
  • whether a related record exists
  • a default value derived from data that is not on the form
  • a flag that several client scripts need on load

Display Business Rule:

(function executeRule(current, previous) {
  g_scratchpad.hasOpenTasks = false;

  var task = new GlideRecord("incident_task");
  task.addQuery("incident", current.sys_id);
  task.addActiveQuery();
  task.setLimit(1);
  task.query();

  if (task.next()) {
    g_scratchpad.hasOpenTasks = true;
  }
})(current, previous);

Client Script:

function onLoad() {
  if (g_scratchpad.hasOpenTasks) {
    g_form.addInfoMessage("This incident still has active incident tasks.");
  }
}

This is better than making a client-side round trip after the form loads just to discover something that was already knowable on the server.

Use g_scratchpad when:

  • the data is needed immediately on load
  • the value can be determined before the form is rendered
  • the same value is reused by multiple client-side checks

3. Use asynchronous GlideAjax for on-demand lookups

If the data depends on something the user does after the form loads, GlideAjax is the right tool.

Examples:

  • the user changes caller_id
  • the user selects a CI and you need related server-side data
  • the check is too expensive or too dynamic to preload in g_scratchpad

Example:

function onChange(control, oldValue, newValue, isLoading) {
  if (isLoading || !newValue) {
    return;
  }

  var ga = new GlideAjax("UserLookupAjax");
  ga.addParam("sysparm_name", "getManagerName");
  ga.addParam("sysparm_user_id", newValue);
  ga.getXML(function (response) {
    var answer = response.responseXML.documentElement.getAttribute("answer");
    g_form.setValue("u_manager_name", answer || "");
  });
}

The important part is not only using GlideAjax, but using it asynchronously. Let the UI continue while the server response comes back.

4. Do not make the same lookup again and again

One of the easiest ways to create unnecessary load is calling the server repeatedly for the same answer.

Common examples:

  • an onChange script that fires several times for the same value
  • multiple client scripts calling the same Script Include separately
  • a script that re-fetches data that was already available on load

If the answer can be reused during the same session, cache it.

var departmentCache = {};

function loadDepartmentName(departmentId) {
  if (departmentCache[departmentId]) {
    g_form.setValue("u_department_name", departmentCache[departmentId]);
    return;
  }

  var ga = new GlideAjax("DepartmentAjax");
  ga.addParam("sysparm_name", "getDepartmentName");
  ga.addParam("sysparm_department_id", departmentId);
  ga.getXML(function (response) {
    var answer = response.responseXML.documentElement.getAttribute("answer") || "";
    departmentCache[departmentId] = answer;
    g_form.setValue("u_department_name", answer);
  });
}

Sometimes the right optimization is not a faster API call. It is making fewer calls.

5. Return only what the client actually needs

A client lookup should be narrow.

If the client needs one boolean, return one boolean. If it needs two or three related values, return a small JSON payload. Avoid building server calls that fetch a full record just to support one tiny UI decision.

That keeps the Script Include simpler and makes the client logic easier to reason about.

6. A practical rule of thumb

Use this default:

  • If the data is already on the form, stay client-side.
  • If the data is needed immediately and can be known before render, use g_scratchpad.
  • If the data is needed later based on user interaction, use asynchronous GlideAjax.

That simple split prevents a lot of unnecessary traffic and usually makes the script easier to read.

Final thought

Client scripting is not only about making the form behave correctly. It is also about deciding where the work should happen.

The best client scripts avoid server calls when possible, preload the important data when sensible, and use asynchronous GlideAjax only when the lookup is truly on demand.

comments powered by Disqus

Related