Any fancy method to wrap code in such a way you can use same syntax for both client/server script
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
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
| 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 |
I recognize there may be some limitations but..
see below
--------------------
Main User-Related Equivalents
| Current user sys_id | gs.getUserID() | g_user.userID | Exact match |
| Username (login name) | gs.getUserName() | g_user.userName | Exact |
| First name | gs.getUser().getFirstName() | g_user.firstName | Exact |
| Last name | gs.getUser().getLastName() | g_user.lastName | Exact |
| Full / Display name | gs.getUserDisplayName() or gs.getUser().getFullName() | g_user.fullName (or firstName + ' ' + lastName) | Mostly exact |
| gs.getUser().getEmail() | None | Not cached in g_user | |
| Has role | gs.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 roles | gs.hasRole('itil', 'admin') | g_user.hasRoles('itil', 'admin') | Similar |
| Is member of group | gs.getUser().isMemberOf('Service Desk') | None | Server only |
| List of my groups | gs.getUser().getMyGroups() | None | Server only |
| Department / Company / Manager | gs.getUser().getDepartmentID() (and similar) | None | Server only |
| Any other user field | gs.getUser().getRecord().getValue('title') | None (or query) | Prefer server |
| Full user object | gs.getUser() | Limited cached properties only | Full object is server-only |
Other Common Pairs
| Info / Log message | gs.info(), gs.log(), gs.addInfoMessage() | console.log(), g_form.addInfoMessage(), jslog() |
| Error message | gs.addErrorMessage() | g_form.addErrorMessage() |
| System property | gs.getProperty('name') | Via g_scratchpad or GlideAjax |
| Form field get/set | current.field (GlideRecord) | g_form.getValue() / g_form.setValue() |
| Show/hide field | — | g_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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday - last edited 34m ago
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)