Claude Media
Claude CodeでGlideRecordを非同期に直す — ServiceNowのClient Script

Claude CodeでGlideRecordを非同期に直す — ServiceNowのClient Script

Client Scriptの同期的なGlideRecord呼び出しを、GlideAjaxやgetReferenceのコールバックへ置き換える依頼の出し方です。洗い出しは読み取り専用のサブエージェントに任せます。

Client Scriptの中でGlideRecordを使い、サーバーの結果を待って処理を進める書き方は、画面が固まる原因になります。これを非同期に直す作業は、Claude Codeに任せやすい部類です。ただし「非同期にして」と一言で頼むと、置換先の選び方も、サーバー側に必要な変更も、Claude Codeの推測に回ってしまいます。

この記事では、洗い出し、置換先の振り分け、サーバー側の部品、依頼文の型を順に示します。洗い出しは読み取り専用のサブエージェントに分けて任せます。

何を直すのか — 公式ガイドの制約から決める

ServiceNow SDKのクライアントサイドAPIガイドは、制約として次の趣旨を挙げています。同期のサーバー呼び出しを避け、GlideAjaxを非同期のコールバックで使うこと。getXMLWait()はUIを固めるうえ、スコープ付きアプリでは非推奨であること。

つまり直す方向は2つです。

  • サーバーの問い合わせ全般は、GlideAjaxとコールバックに寄せる
  • 参照フィールドの先のレコードを1件読むだけなら、getReference(field, callback)を使う

getReferenceは、同ガイドの参照の項に載っています。ただし説明は「レガシー。複雑な検索にはGlideAjaxを推奨」です。参照先の値を1つ2つ読む程度なら足ります。条件検索や複数件の取得はGlideAjaxに回す、という振り分けになります。

クライアント側のGlideRecordそのものについて、このガイドに記述はありません。そのため依頼文では「ガイドの制約に照らして、同期的に待つ呼び出しを直す」と書きます。ガイドに無い判断をClaude Codeに任せない、という線引きです。

先に洗い出す — 読み取り専用のサブエージェント

置換の前に、直す対象の一覧が要ります。Client Scriptは数が多く、検索結果がメインの会話を埋めやすい作業です。サブエージェントは、検索結果を自分のコンテキストで消化して要約だけを返すので、この洗い出しに向いています。

サブエージェントはYAMLのfrontmatterとMarkdownで定義します。必須はnameとdescriptionだけで、toolsを絞れば読み取り専用にできます。公式ドキュメントのコードレビュー役も、Read, Grep, Glob, Bashだけを渡し、EditとWriteを外した例です。ここでは書き込みを持たせない構成にします。

---
name: servicenow-client-auditor
description: Client Scriptのサーバー同期呼び出しを洗い出す。ServiceNowの画面スクリプトを調べるときに使う
tools: Read, Grep, Glob
model: inherit
---
 
src配下のクライアントスクリプト(Now.includeで参照される.jsを含む)を調べ、
GlideRecord / getXMLWait / getReference(コールバックなし)を使う箇所を列挙する。
 
各箇所について次を表で返す。
- ファイルと行
- スクリプトの種類(onLoad / onChange / onSubmit / onCellEdit)
- 何のためにサーバーへ問い合わせているか
- 置換先の候補(getReference か GlideAjax か)
 
ファイルは変更しない。

toolsの書き方は、カンマ区切りの文字列かYAMLのリストです。省略すると、使えるツールをすべて引き継ぎます。読み取り専用にしたいなら、省略せずに明示します。

呼び出しは「servicenow-client-auditorで洗い出して」のように名前を出せば足ります。確実に走らせたいときは、@で選ぶ方法もあります。

置換先を振り分ける

洗い出しの表を受け取ったら、置換先を決めます。判断の軸は、サーバーに何を聞いているかです。

元の用途置換先理由
参照フィールドの先の値を1つ読む置換先g_form.getReference(field, callback)理由参照の項に載るAPI。単純な読み取りに足りる
条件を付けて検索する・複数件を取る置換先GlideAjax + クライアント呼び出し可能なスクリプトインクルード理由複雑な検索はGlideAjaxが推奨されている
値の検証でサーバー確認が要る(onSubmit)置換先GlideAjaxの結果を待ってから送信を判断理由下の「つまずき」を参照
getXMLWait()置換先GlideAjaxのコールバック理由UIを固める。スコープ付きアプリでは非推奨

サーバー側を用意する

GlideAjaxに寄せるなら、サーバー側のスクリプトインクルードが要ります。Claude Codeに「クライアントだけ直して」と頼むと、これを作り忘れた形で返ってくることがあります。

スクリプトインクルード側の条件は、SDKのガイドに次のとおり書かれています。

  • Fluent定義でclientCallable: trueを指定する。指定が無いとGlideAjaxの呼び出しは拒否される
  • JavaScript側はglobal.AbstractAjaxProcessorを継承する
  • クライアントからの引数はthis.getParameter('sysparm_name')のように読む
  • 構造化した結果はJSON文字列で返す
  • Class.create()のtypeは、クラス名とFluent定義のnameに一致させる

ガイドの例にあるクライアント呼び出し可能なスクリプトインクルードは、次の形です(JavaScript側、抜粋)。

var TodoAjax = Class.create()
 
TodoAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
    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',
})

サーバー側ではGlideRecordが通常どおり使えます。クライアントで直接叩いていた検索を、サーバーのメソッドに移すイメージです。Fluent側の定義は、ScriptInclude({ ..., clientCallable: true })としてNow.includeでJavaScriptを参照します。全体の流れはClaude CodeでServiceNow SDKのFluentアプリを作るにまとめています。

クライアント側の書き換えの形

置換後のクライアントは、結果を受け取るコールバックの中で後続の処理を行う形になります。次は、変更の形を示す例です。メソッド名は、手元のインスタンスのバージョンで確かめてください。

// 変更前の発想: 検索結果を待ってから、次の行で使う
// 変更後: 結果を受け取るコールバックの中に、後続の処理を移す
function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading) return   // フォーム読み込み中は何もしない
 
    var ga = new GlideAjax('TodoAjax')
    ga.addParam('sysparm_name', 'getTasks')
    ga.getXMLAnswer(function (answer) {
        var tasks = JSON.parse(answer)
        g_form.setValue('u_count', String(tasks.length))
    })
}

if (isLoading) returnは、SDKのガイドが挙げる制約です。onChangeはフォーム読み込み時にも走るため、冒頭で止めるよう求めています。

直すときの落とし穴は、「待つ」前提で書かれた後続コードです。同期のときは、呼び出しの次の行で結果が使えます。コールバックに変えると、次の行の時点では結果がありません。結果を使う処理は、すべてコールバックの中へ移します。

参照フィールドの読み取りをgetReferenceに直す

単純な読み取りは、getReferenceで済みます。依頼文でこの形を指定しておくと、不要にGlideAjaxとスクリプトインクルードが増えるのを防げます。次は変更の形を示す例です。

function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading || newValue === '') return
 
    // 参照フィールド caller_id の先のレコードを、コールバックで受け取る
    g_form.getReference('caller_id', function (caller) {
        // 結果を使う処理は、すべてこの中に置く
        if (caller && caller.vip === 'true') {
            g_form.setValue('priority', '2')
        }
    })
}

コールバックの中で参照できる項目は、参照先レコードの項目名です。vipのような項目名は、手元のインスタンスのテーブル定義で確かめてください。Claude Codeに頼むときは、フィールド名を推測させず、洗い出しの表に書かれた実名を使わせます。

ブラウザで動くスクリプトに、サーバーの書き方を持ち込ませない

直す過程で、Claude Codeがサーバー側のスクリプトの癖を持ち込むことがあります。SDKのガイドは、クライアントスクリプトについて次の制約を挙げています。

  • import / exportは使えない。外部の.jsはNow.include()で参照する
  • サードパーティのライブラリは使えない。使えるのはg_form・g_user・GlideAjaxなどの、プラットフォームが提供するAPIだけ
  • テーブル名は、スコープ付きの完全名で書く

サーバー側のスクリプトインクルードはモジュールで書く流れが推奨されますが、ブラウザで動くクライアントスクリプトはそうではありません。ガイドは両者を分けて説明しています。依頼文かCLAUDE.mdに「クライアントスクリプトにimportを書かない」と一行入れておくと、このずれを防げます。

また、サーバー側のスクリプトインクルードのクラスファイルには、Glide APIのimportを書きません。Glide APIは、スクリプトインクルードの実行コンテキストで自動的に使えるためです。型定義の補完を効かせたいがためにimportを足す変更は、ここで止めます。

直した後のレビューも分ける

書き換え後の確認は、書いたClaude Codeとは別のサブエージェントに読ませると、思い込みが入りにくくなります。公式ドキュメントのコードレビュー役の例は、起動したらgit diffで最近の変更を見るところから始めています。同じ作りで、観点をこの作業に絞ります。

  • コールバックの外で、結果の変数を使っていないか
  • onChangeの先頭にisLoadingのガードがあるか
  • GlideAjaxで呼ぶメソッド名が、サーバー側に実在するか
  • スクリプトインクルードにclientCallable: trueが付いているか
  • typeがクラス名と一致しているか

一覧にした観点を渡し、指摘だけを返させます。修正までさせないほうが、変更の責任が分かれます。サブエージェントのトークンはメインの会話と同じ利用上限に数えられるため、対象が数本のうちは洗い出しとレビューを1本のサブエージェントにまとめる手もあります。

Claude Codeへの依頼文の型

依頼文には、対象・置換先の方針・サーバー側の扱い・完了条件を入れます。

servicenow-client-auditorで、src配下のClient Scriptから
サーバーを同期的に待つ呼び出しを洗い出してください。
そのうえで次の方針で直します。
- 参照フィールドの値を1つ読むだけなら getReference(field, callback)
- 条件検索・複数件取得は GlideAjax。必要なスクリプトインクルードは
  clientCallable: true で追加し、AbstractAjaxProcessor を継承する
- 結果を使う処理はコールバックの中へ移す
- onChange の先頭で isLoading を確認する
1ファイルずつ直し、直すたびに npm run build を実行してください。

完了条件にbuildを置くのは、Fluentアプリの記事と同じ考え方です。ただしbuildが通っても、画面の動作は保証されません。ビルドが見るのは定義の整合で、コールバックの順序は見ないためです。動作確認は、インスタンス上で人が行う前提にします。

CLAUDE.mdには、次の2行を足しておくと毎回の説明が減ります。

- Client Scriptでサーバーを同期的に待つ呼び出し(GlideRecord / getXMLWait)を新規に書かない
- クライアントからサーバーを呼ぶときは GlideAjax のコールバックを使い、サーバー側は clientCallable: true にする

インスタンスへのinstallだけは確認を挟む設定にしておくと安全です。書き方はClaude Codeの/permissionsコマンドにあります。

つまずきやすい点

onSubmitで結果を待てない

onSubmitは、falseを返すと送信を取り消すスクリプトです。非同期にすると、コールバックが返る前にtrueで抜けてしまい、検証の意味がなくなります。サーバー確認が必須のonSubmitは、機械的な置換では済みません。確認の結果を待つ設計か、サーバー側のBusiness Ruleで検証する設計か、先に方針を決めてから依頼します。

clientCallableの付け忘れ

指定が無いと、GlideAjaxの呼び出しは拒否されます。SDKのガイドは、呼び出しが静かに失敗するとも書いています。置換後に何も起きない場合は、まずここを確かめます。

型名の不一致

typeプロパティが、クラス名とFluent定義のnameと食い違うと、実行時に失敗します。Claude Codeがクラス名だけ変えたときに起きやすい点です。

onLoadで値の反映が遅れる

onLoadで取得した値をフィールドに入れる処理は、コールバックが返るまで反映されません。フォームを開いた直後は、一瞬だけ初期値のままの画面になります。同期のときはこの遅れが画面の固まりとして現れていたので、見え方が変わるだけです。気になる項目は、コールバックの中で表示を切り替える処理も一緒に移します。

まとめ

クライアント側GlideRecordの是正は、洗い出し・振り分け・サーバー側の追加・コールバックへの移し替えの4段で依頼します。洗い出しは読み取り専用のサブエージェントに分け、置換は1ファイルずつbuildを挟みます。onSubmitだけは設計の判断が要るので、機械的に置換させない扱いが現実的です。

インスタンスとの接続側の話はServiceNowをClaude CodeやCoworkで使う手順にあります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. servicenow.github.io/sdk/guides/client-side-api-guide
  2. servicenow.github.io/sdk/guides/script-include-guide
  3. servicenow.github.io/sdk/guides/client-script-guide
  4. code.claude.com/docs/en/sub-agents
この記事を共有:XはてブLinkedIn