‹ All essays

ServiceNow

Building ServiceNow Apps with the Now SDK and Fluent (TypeScript, Source-Control-First)

Two ways to build on ServiceNow

For years, building an application on ServiceNow meant clicking through the platform UI — Studio, App Engine Studio, the form designer — and letting the instance store every artifact as a record. That still works. But ServiceNow now ships a second, complementary path: the Now SDK (also called the ServiceNow SDK), where you describe your application as TypeScript source files on your own machine using a declarative library called Fluent, then build and deploy it to an instance.

This article is a practical, code-first tour of that workflow, grounded in the official ServiceNow/sdk-examples repository, the Australia-family Fluent docs, and npx @servicenow/sdk explain (CLI docs for Fluent APIs).

The one-line summary: Fluent lets you define tables, business rules, script includes, ACLs, REST APIs and more as version-controlled code, so a ServiceNow app can live in Git and be built in a normal CI pipeline — like any other software project.

Why a code-first SDK exists

  • Real source control. Update Sets and even scoped-app Git integration are record-centric. With the SDK, your app is the source tree — clean diffs, pull requests, and code review work the way engineers expect.
  • Local development. Author in VS Code with TypeScript types, autocomplete, and your own linters and formatters. No round-trip to the instance to write a Business Rule.
  • Repeatable builds. A build step turns your TypeScript into deployable metadata, so the same commit produces the same app on dev, test, and prod.
  • It complements Studio — it does not replace it. You can still open the app in the platform; the SDK simply gives a code path for teams that want one.

Prerequisites

  • Node.js v20+ and a package manager (the samples use pnpm; npm works too).
  • The SDK packages: @servicenow/sdk, @servicenow/glide (server-side type definitions), and typescript.
  • Access to a ServiceNow instance where you can authenticate and deploy a scoped application.

Anatomy of an SDK project

Every SDK app is a normal Node project. The pieces that matter:

my-app/
  now.config.json          // app scope + build config
  package.json             // deps + the "now-sdk build" script
  src/
    fluent/                // your Fluent (*.now.ts) declarations
      index.now.ts
      generated/keys.ts    // auto-generated stable IDs (do not hand-edit)
    server/                // server-side .js used by rules/script includes

The now.config.json pins the application scope so builds are deterministic:

{
  "scope": "x_myapp",
  "scopeId": "fc1b5713c3db3110d6489a038a40dd84",
  "serverModulesDir": "src/server"
}

And package.json wires up the build command:

{
  "name": "my-app",
  "scripts": {
    "build": "now-sdk build"
  },
  "devDependencies": {
    "@servicenow/sdk": "latest",
    "@servicenow/glide": "latest",
    "typescript": "latest"
  }
}

Fluent by example

Fluent gives you a typed function for each platform artifact. You import it from @servicenow/sdk/core, call it with a configuration object, and the build turns that into the corresponding record(s) in your scoped app. The magic identifier Now.ID['...'] assigns a stable, generated key so re-deploys update the same record instead of creating duplicates.

1. A table

import { DateTimeColumn, IntegerColumn, StringColumn, Table } from '@servicenow/sdk/core'

export const x_myapp_order = Table({
    name: 'x_myapp_order',
    label: 'Order',
    schema: {
        order_number: StringColumn({ label: 'Order Number', mandatory: true }),
        quantity: IntegerColumn({ label: 'Quantity', mandatory: true }),
        ordered_at: DateTimeColumn({ label: 'Ordered At' }),
    },
})

2. A Business Rule

JavaScript modules are the modern, preferred approach for server-side logic in Fluent projects (typed Glide APIs via @servicenow/glide, import/export, reuse). Now.include() still inlines a file at build time when you want a classic script field.

import { BusinessRule } from '@servicenow/sdk/core'
import { setOrderPriority } from '../server/set-order-priority'

BusinessRule({
    $id: Now.ID['set_order_priority'],
    name: 'Set Order Priority',
    active: true,
    table: 'x_myapp_order',
    when: 'before',
    action: ['insert', 'update'],
    script: setOrderPriority,
})

The module under src/server/ is ordinary ServiceNow server JavaScript — the same GlideRecord / gs APIs you already know — with optional TypeScript types from @servicenow/glide.

3. A Script Include

import { ScriptInclude } from '@servicenow/sdk/core'

ScriptInclude({
    $id: Now.ID['order-utils'],
    name: 'OrderUtils',
    active: true,
    apiName: 'x_myapp.OrderUtils',
    script: Now.include('./OrderUtils.server.js'),
})

4. An ACL

Security is code too. Roles and record ACLs are declared alongside everything else, so access control ships and reviews with the feature it protects:

import { Acl, Role } from '@servicenow/sdk/core'

export const orderManager = Role({ name: 'x_myapp.order_manager' })

Acl({
    $id: Now.ID['order_write_acl'],
    type: 'record',
    operation: 'write',
    table: 'x_myapp_order',
    roles: [orderManager],
})
The sdk-examples repo has focused samples for each of these — plus Scripted REST APIs, UI Actions, Script Actions, Flows, ATF tests, cross-scope modules, and React / Svelte / Vue / SolidJS UI pages. Cloning it and running pnpm install is the fastest way to see real, buildable Fluent.

Build and deploy

The everyday loop is small:

# scaffold a new app (interactive, or use --appName/--scopeName flags)
npx @servicenow/sdk init

# type-check + transform Fluent into deployable metadata
npx @servicenow/sdk build

# authenticate, then install the built app on the instance
# (CLI alias: now-sdk deploy → now-sdk install)
npx @servicenow/sdk auth --add <instance-url> --type basic
npx @servicenow/sdk install

Because the output is deterministic, the same command runs in CI: install dependencies, now-sdk build, run your ATF tests, then deploy to the target instance. That is the whole point — a ServiceNow app that behaves like any other codebase.

SDK / Fluent vs App Engine Studio — when to use which

Use the Now SDK + Fluent when…Use App Engine Studio / UI when…
The team is engineering-led and wants Git, PRs, and code reviewCitizen developers or admins are building quickly in-platform
You need repeatable CI/CD builds across environmentsA single instance, low change velocity, Update Sets are enough
You want TypeScript types, local linting, and diff-able sourceYou prefer guided, low-code form and flow designers
The app is large, long-lived, and shared across a teamThe app is small or experimental

They are not mutually exclusive: an app built with the SDK still opens in the platform, and you can adopt the SDK for the parts of an app that benefit most from source control.

Gotchas worth knowing

  • Do not hand-edit generated/keys.ts. Those stable IDs are what let re-deploys update existing records instead of duplicating them.
  • Prefer server modules; classic scripts still follow platform rules. Fluent is modern TypeScript, but server modules and inlined scripts still run under the platform engine — keep scoped-vs-global scripting rules in mind. Never invent sys_ids; always use Now.ID['key'].
  • Scope is fixed by now.config.json. Changing scope after records exist is disruptive; decide the scope up front.
  • The SDK evolves quickly. Pin your @servicenow/sdk version and check the release notes before upgrading — argument shapes and available components expand release over release.

Where to go next

If you already know GlideRecord and scoped apps, the SDK is not a new platform to learn — it is a cleaner front door to the one you already use.