---
tags: [script include, sys_script_include, server-side, reusable logic, GlideAjax, client callable, Class.create, AbstractAjaxProcessor, module bridge, require, facade, legacy, sandboxCallable, sandbox callable, sandbox]
---
# Script Includes

Guide for creating ServiceNow Script Includes using the Fluent API. Script includes bundle reusable server-side logic into classes or utilities that can be called from business rules, scheduled scripts, other script includes, and client-side code via GlideAjax.

## When to Use

- Creating reusable server-side logic (utility classes, helper functions)
- Building server-side APIs callable from client code via GlideAjax
- Sharing business logic across multiple business rules, scheduled scripts, or other server scripts
- Creating AJAX processors for UI Pages or client scripts

## Instructions

1. **Use `Now.include` for scripts:** The ScriptInclude API only accepts strings, so keep JavaScript in a standalone file and reference it with `Now.include('../../server/script-includes/file.js')`. This enables syntax highlighting and two-way sync. Note: Script Include class files (`Class.create()`) should NOT import Glide APIs — they are auto-available in the Script Include execution context.
2. **Use ES5 syntax:** Use the classic `Class.create()` pattern or plain functions. ES6 `class` syntax is not supported on the ServiceNow platform for script includes.
3. **Match `type` to class name:** In the `Class.create()` prototype, the `type` property must exactly match the class name and the `name` property in the Fluent definition. Mismatches cause runtime failures.
4. **Set `clientCallable` for GlideAjax:** If client-side code needs to call the script include, set `clientCallable: true`. Without this, GlideAjax calls will be rejected.
5. **Set `sandboxCallable` only when needed:** Set `sandboxCallable: true` only when the script include will be called from sandbox-evaluated expressions such as `javascript:` query conditions, filter expressions, or column default values. Do not set it by default — it widens the security surface.
6. **Extend `AbstractAjaxProcessor` for AJAX:** When the script include is called via GlideAjax, extend `global.AbstractAjaxProcessor` and use `this.getParameter()` to read client parameters.
7. **Scope accessibility:** Set `accessibleFrom: 'public'` only when other scoped apps need access. Default to `'package_private'` for internal use.

## Key Concepts

### Class-Based vs Classless

- **Class-based** (most common): Use `Class.create()` with a prototype. Best for grouping related methods. The `type` property in the prototype must match the class name.
- **Classless (on-demand)**: A single function that runs when called by name. Best for one-off utility functions. The function name must match the `name` property.

### GlideAjax Pattern

To make a script include callable from client-side code:

1. Set `clientCallable: true` in the Fluent definition
2. Extend `global.AbstractAjaxProcessor` in the JavaScript
3. Use `this.getParameter('sysparm_name')` to read parameters passed from the client
4. Return data as JSON strings for structured responses

### Sandbox Callable Pattern

When a sandbox-evaluated expression (e.g., a scheduled script `condition`, `javascript:` filter, or column default value) needs complex logic that can't be written as a single expression, create a `sandboxCallable` script include to hold that logic. The sandbox expression then calls it as a single expression:

- Condition field: `new global.MyConditionChecker().shouldRun()`
- Filter query: `javascript:new global.MyFilterHelper().getFilterValue()`

The script include body runs in the normal server-side context where `var`, `if`, loops, and GlideRecord are fully supported. Only the calling expression is restricted to a single expression.

Fluent definition (`src/fluent/script-includes/sync-condition-checker.now.ts`):

```typescript fluent
import '@servicenow/sdk/global'
import { ScriptInclude } from '@servicenow/sdk/core'
import { ScriptInclude } from '@servicenow/sdk/core'

ScriptInclude({
    $id: Now.ID['SyncConditionChecker'],
    name: 'SyncConditionChecker',
    script: Now.include('../../server/script-includes/sync-condition-checker.js'),
    description: 'Checks whether daily sync should run',
    sandboxCallable: true,
})
```

JavaScript logic (`src/server/script-includes/sync-condition-checker.js`):

```javascript
var SyncConditionChecker = Class.create()
SyncConditionChecker.prototype = {
    initialize: function () {},
    shouldRun: function () {
        if (gs.getProperty('x_myapp.sync.enabled', 'false') !== 'true') {
            return false
        }
        var dayOfWeek = new GlideDateTime().getDayOfWeekLocalTime()
        if (dayOfWeek < 2 || dayOfWeek > 6) {
            return false
        }
        var gr = new GlideRecord('x_myapp_queue')
        gr.addQuery('processed', false)
        gr.setLimit(1)
        gr.query()
        return gr.hasNext()
    },
    type: 'SyncConditionChecker',
}
```

### Project Structure

```
src/
  server/
    script-includes/
      my-utils.js           <-- JavaScript business logic
  fluent/
    script-includes/
      my-utils.now.ts       <-- Fluent record definition
```

### Bridging Modules Through Script Includes

Script includes have been the standard way to write reusable server-side code in ServiceNow for years. JavaScript modules are the modern replacement, but many platform features still rely on script includes — GlideAjax, cross-scope APIs, script include-based extension points, and integrations that call script includes by name. When your logic lives in a module but needs to be accessible through one of these legacy mechanisms, create a script include that acts as a thin bridge.

**When to use this pattern:**

- You have module code that needs to be callable via GlideAjax from client scripts
- Another scoped app needs to call your logic by script include name
- A platform feature (e.g., dynamic reference qualifier, condition script) expects a script include
- You want to keep your business logic in a module (with typed imports, testability, and code reuse) while still being accessible to legacy callers

**How it works:**

1. Write your business logic in a module file with ES module syntax (`import`/`export`)
2. Create a thin Script Include wrapper that uses `require()` to load the module and delegates to it
3. Define the Fluent record pointing at the wrapper via `Now.include()`

The wrapper `.js` file uses `Class.create` and calls `require()` to pull in the bundled module from `./dist/modules/`. At build time, the SDK bundles your module into the `dist/modules/` directory using repack. At runtime on the platform, `require()` resolves to that bundled output.

**Example — exposing a module through a script include:**

Module file (`src/modules/server/string-utils.js`):

```javascript
export function capitalize(text) {
    if (!text) return ''
    return text.charAt(0).toUpperCase() + text.substring(1)
}

export function truncate(text, maxLength) {
    if (!text || text.length <= maxLength) return text
    return text.substring(0, maxLength) + '...'
}
```

Wrapper script (`src/server/script-includes/string-utils.js`):

```javascript
var StringUtils = Class.create()

StringUtils.prototype = {
    initialize: function () {
        this._mod = require('./dist/modules/server/string-utils.js')
    },

    capitalize: function (text) {
        return this._mod.capitalize(text)
    },

    truncate: function (text, maxLength) {
        return this._mod.truncate(text, maxLength)
    },

    type: 'StringUtils',
}
```

Fluent definition (`src/fluent/script-includes/string-utils.now.ts`):

```typescript fluent
import '@servicenow/sdk/global'
import { ScriptInclude } from '@servicenow/sdk/core'

ScriptInclude({
    $id: Now.ID['StringUtils'],
    name: 'StringUtils',
    script: Now.include('../../server/script-includes/string-utils.js'),
    description: 'Bridge to string utility module',
})
```

**Key rules for the wrapper:**

- Keep the wrapper as thin as possible — it should only `require()` the module and delegate. All business logic belongs in the module.
- The wrapper uses `Class.create` and must NOT import Glide APIs (they are auto-available in Script Include context).
- The module file MUST import Glide APIs from `@servicenow/glide` (they are NOT auto-available in module context).
- The `require()` path points to `./dist/modules/...` — this is the bundled output location. Match the path structure of your module source file under `src/modules/`.
- The `type` property, class name, and Fluent `name` must all match exactly.

## Avoidance

- **Never use ES6 `class` syntax** -- the ServiceNow server runtime does not support it for script includes
- **Never mismatch `type` and class name** -- the `type` property in the prototype, the `Class.create()` variable name, and the `name` in the Fluent definition must all match exactly
- **Never forget `clientCallable: true` for GlideAjax** -- client calls will silently fail without it
- **Avoid inline scripts** for anything beyond a one-liner -- use `Now.include()` for maintainability and syntax highlighting

## API Reference

For the full property reference, see the `scriptinclude-api` topic.

## Examples

### Basic Script Include

Fluent definition (`src/fluent/script-includes/math-utils.now.ts`):

```typescript fluent
import '@servicenow/sdk/global'
import { ScriptInclude } from '@servicenow/sdk/core'

export const MathUtils = ScriptInclude({
    $id: Now.ID['MathUtils'],
    name: 'MathUtils',
    script: Now.include('../../server/script-includes/math-utils.js'),
    description: 'Basic math utility functions',
    accessibleFrom: 'package_private',
})
```

JavaScript logic (`src/server/script-includes/math-utils.js`):

> **Important:** Script Include class files (`Class.create` pattern) must NOT import Glide APIs — they are auto-available in the Script Include execution context. See the `module-guide` topic.

```javascript
var MathUtils = Class.create()
MathUtils.prototype = {
    initialize: function () {},

    multiply: function (a, b) {
        return a * b
    },

    type: 'MathUtils', // IMPORTANT: must match the class name
}
```

### GlideAjax Script Include (Client-Callable)

Fluent definition (`src/fluent/script-includes/todo-ajax.now.ts`):

```typescript fluent
import '@servicenow/sdk/global'
import { ScriptInclude } from '@servicenow/sdk/core'

export const TodoAjax = ScriptInclude({
    $id: Now.ID['TodoAjax'],
    name: 'TodoAjax',
    script: Now.include('../../server/script-includes/todo-ajax.js'),
    description: 'AJAX processor for to-do operations',
    clientCallable: true,
    accessibleFrom: 'public',
})
```

JavaScript logic (`src/server/script-includes/todo-ajax.js`):

```javascript
var TodoAjax = Class.create()

TodoAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
    getTasks: function () {
        var tasks = []
        var gr = new GlideRecord('x_snc_todo_item')
        gr.query()

        while (gr.next()) {
            tasks.push({
                sys_id: gr.getUniqueValue(),
                task: gr.getValue('task'),
                state: gr.getValue('state'),
            })
        }

        return JSON.stringify(tasks)
    },

    addTask: function () {
        var task = this.getParameter('sysparm_task')
        var state = this.getParameter('sysparm_state') || 'ready'

        var gr = new GlideRecord('x_snc_todo_item')
        gr.initialize()
        gr.setValue('task', task)
        gr.setValue('state', state)
        var sysId = gr.insert()

        return JSON.stringify({
            success: true,
            task: { sys_id: sysId, task: task, state: state },
        })
    },

    type: 'TodoAjax',
})
```
