Some PDIs are currently unavailable, and PDI actions are paused. View the latest updates here. Read More

Any fancy method to wrap code in such a way you can use same syntax for both client/server script

EltonA
Tera Contributor

 

 

Any fancy method to wrap code in such a way you can use same syntax for both client/server script

 

First of all you may ask why.. 

Known limitations of the below:

Important limitations

What you want Possible with identical syntax? Notes
Basic props (ID, name, first/last, hasRole)YesPerfect for the facade
Email, groups, department, custom fieldsNoNot present on g_user. You still need GlideAjax or Display BR + g_scratchpad on client
hasRoleExactlyClient onlyServer has different behaviour around elevated privileges
Async needs (extra data)Syntax divergesClient becomes promise/callback based
Security-critical checksPrefer server onlyClient checks are easily bypassed

 

I recognize there may be some limitations but..

see below 

--------------------

Main User-Related Equivalents

Purpose / Functionality Server-side (gs) Client-side Notes
Current user sys_idgs.getUserID()g_user.userIDExact match
Username (login name)gs.getUserName()g_user.userNameExact
First namegs.getUser().getFirstName()g_user.firstNameExact
Last namegs.getUser().getLastName()g_user.lastNameExact
Full / Display namegs.getUserDisplayName() or gs.getUser().getFullName()g_user.fullName (or firstName + ' ' + lastName)Mostly exact
Emailgs.getUser().getEmail()NoneNot cached in g_user
Has rolegs.hasRole('itil')g_user.hasRole('itil')Client version returns true if user has admin
Has exact role (ignore admin)g_user.hasRoleExactly('itil')Client-only
Has any of multiple rolesgs.hasRole('itil', 'admin')g_user.hasRoles('itil', 'admin')Similar
Is member of groupgs.getUser().isMemberOf('Service Desk')NoneServer only
List of my groupsgs.getUser().getMyGroups()NoneServer only
Department / Company / Managergs.getUser().getDepartmentID() (and similar)NoneServer only
Any other user fieldgs.getUser().getRecord().getValue('title')None (or query)Prefer server
Full user objectgs.getUser()Limited cached properties onlyFull object is server-only
 
 
 

Other Common Pairs

Purpose Server-side Client-side
Info / Log messagegs.info(), gs.log(), gs.addInfoMessage()console.log(), g_form.addInfoMessage(), jslog()
Error messagegs.addErrorMessage()g_form.addErrorMessage()
System propertygs.getProperty('name')Via g_scratchpad or GlideAjax
Form field get/setcurrent.field (GlideRecord)g_form.getValue() / g_form.setValue()
Show/hide fieldg_form.setVisible('field', true/false)
Make field read-only

g_form.setReadOnly('field', true)

 

 

 

--------------

 

Important Notes

  • gs does not exist on the client. Using it in a Client Script or UI Policy will break the script.
  • g_user is a lightweight, read-only cached subset of the current user. It is available in Client Scripts, UI Policies, and client UI Actions.
  • Data not available on the client (email, groups, department, custom fields, system properties, etc.):
    • Best option for form load: Display Business Rule → put values into g_scratchpad.xxx = value; → read them in an onLoad Client Script.
    • On-demand: Use GlideAjax to call a Script Include.
  • Always do security / authorization checks on the server. Client-side checks (g_user.hasRole, etc.) are for UX only and can be bypassed.

This covers the common “get user etc.” cases developers mix up most often.

---------

 

 

**Yes, but only approximately — and only for the overlapping subset of methods.**

There is **no official ServiceNow API** that gives you truly identical executable syntax on both server and client. The two environments are completely separate (server-side Rhino/JS vs browser sandbox), so `gs` is simply undefined on the client and `g_user` is undefined on the server.

### Closest practical solution: Thin facade / wrapper

You create a small utility object (or set of functions) that presents the **same method names** in both environments. Your business logic then calls the wrapper with nearly identical syntax.

#### Example – `UserUtil` pattern

**Server-side** (put this in a Script Include):

```javascript
var UserUtil = Class.create();
UserUtil.prototype = {
getID: function() {
return gs.getUserID();
},
getUserName: function() {
return gs.getUserName();
},
getFirstName: function() {
return gs.getUser().getFirstName();
},
getLastName: function() {
return gs.getUser().getLastName();
},
getFullName: function() {
return gs.getUserDisplayName();
},
hasRole: function(role) {
return gs.hasRole(role);
},
// Add more as needed
type: 'UserUtil'
};
```

**Client-side** (put the equivalent in a UI Script, or just paste into Client Scripts / UI Policies):

```javascript
var UserUtil = {
getID: function() {
return g_user.userID;
},
getUserName: function() {
return g_user.userName;
},
getFirstName: function() {
return g_user.firstName;
},
getLastName: function() {
return g_user.lastName;
},
getFullName: function() {
return g_user.fullName || (g_user.firstName + ' ' + g_user.lastName);
},
hasRole: function(role) {
return g_user.hasRole(role); // note: returns true for admin
}
// hasRoleExactly is client-only, so you can add a note or omit it
};
```

Now your calling code looks almost the same in both places:

```javascript
var userId = UserUtil.getID();
var name = UserUtil.getUserName();
if (UserUtil.hasRole('itil')) { ... }
```

### Alternative: Environment-detection helper (for mixed contexts)

Useful in UI Actions (which can run client or server) or shared snippets:

```javascript
function getCurrentUserID() {
if (typeof gs !== 'undefined' && gs.getUserID) {
return gs.getUserID();
}
if (typeof g_user !== 'undefined') {
return g_user.userID;
}
return null;
}

// Same pattern for other methods
```

### Important limitations

| What you want | Possible with identical syntax? | Notes |
|--------------------------------|----------------------------------|-------|
| Basic props (ID, name, first/last, hasRole) | Yes | Perfect for the facade |
| Email, groups, department, custom fields | No | Not present on `g_user`. You still need GlideAjax or Display BR + `g_scratchpad` on client |
| `hasRoleExactly` | Client only | Server has different behaviour around elevated privileges |
| Async needs (extra data) | Syntax diverges | Client becomes promise/callback based |
| Security-critical checks | Prefer server only | Client checks are easily bypassed |

### Recommendation

- For the common “get user ID / name / has role” cases → the thin `UserUtil` facade works well and keeps calling code clean.
- For anything more (email, group membership, system properties, etc.) → accept that the client path will be different (GlideAjax or pre-load via Display Business Rule).
- Keep security and data integrity logic on the server; use the client side only for UX.

 

1 REPLY 1

EltonA
Tera Contributor

I forgot to mention, so this was more out of curiousity, but there does seem to be a lot to memorize, like a different debug function name for a client versus a server script etc, 

I'm assuming people are going to say this method would have so many loopholes it wouldnt be viable, but just curious if there were a more sophisticated solution , that handles the loopholes / known limitations etc

Pretty sure this would break something and not be viable for production, but if someone had a modified version of the client or server side classes, e.g. gs but with helper functions with the same name as client scripts style , which call its own functions.  

Maybe if ServiceNow themselves would make helper functions to make the exact syntax in client vs server script.. lol , but that would basically nullify a ton of documentations etc, unless..  this was a separate helper class entirely and optional to use it, in the same way we can choose (in some cases GlideRecord vs GlideQuery)