n8nのCodeノードは、標準ノードでは表しにくい独自のデータ加工を書くためのノードです。使い始める前に、何件のitemを受け取り、どんな形で返すかを決めます。
一つの設定欄で文字列や計算値を作るだけならExpression、決まった集約や配列の展開なら専用ノードを先に検討してください。Codeが必要な場合は、実行モードに合う返却構造を使い、値・型・ID・元itemへの対応を小さなデータで照合するのが基本です。
Codeを使う処理と実行条件を決める
Codeを選ぶ目安は、独自の判定や構造変換を、入力と出力の組として説明できることです。複数件のデータがあるだけで、必ずCodeを置く必要はありません。
Expression・専用ノード・Codeを比較する
| やりたいこと | まず選ぶ方法 | 選ぶ理由 |
|---|---|---|
| 名前と日付を一つの設定欄でつなぐ | Expression | 値を使う欄の近くで計算を表せる |
| 項目名・固定値・残す項目を設定する | Edit Fields (Set) | 変更内容を画面で追いやすい |
| itemsをまとめる、配列をitemsへ分ける | Aggregate・Split Out | 専用の設定で変換の目的を示せる |
| 独自の型判定や複数項目の加工を組み合わせる | Code | 条件と返却構造をまとめて定義できる |
題材は、入力の数値を一定の規則で分類する作例です。単純な一項目の変更ならEdit Fieldsでも表せますが、欠損・不正値・元データの保持を一つの関数でそろえる練習にはCodeが適しています。
先に「入力は4件」「出力も4件」「idを保持」「amount_valueとamount_kindを追加」と書いておくと、完成後に何を照合するかが明確です。配列を展開するだけの目的なら、Split Outの設定で足りるかを確認しましょう。
JavaScriptを主経路にして版とモードを記録する
ここからのコードは、外部ライブラリを使わないJavaScriptです。CloudとSelf-hostedでは依存ライブラリの利用条件が異なるため、この練習は追加インストールを行わずに進めます。
| モード | コードを実行する単位 | この作例で返すもの |
|---|---|---|
| Run Once for All Items | その回の入力items全体に対して1回 | jsonを持つitemの配列 |
| Run Once for Each Item | 現在の入力itemごとに1回 | jsonを持つ1つのitem |
All Itemsの「1回」は、ワークフローの全実行を通して一度だけ、という意味ではありません。ループ内などでノードが再び呼ばれれば、その呼び出しの入力に対してコードを実行します。
実行前には、n8n本体のバージョン、CodeノードのtypeVersion、選択した言語とモードを控えてください。typeVersionはノードの仕様を識別する値で、本体のバージョン番号とは別です。
公式文書では、旧Python実装のPyodideはn8n 2.0で廃止され、Native Pythonは利用できる変数や依存関係の条件が異なります。古いPython例をJavaScriptやNative Pythonへそのまま移す使い方は避けましょう。
全itemsと各itemの入出力をそろえる
All Itemsでは$input.all()で受け取った配列を処理し、Each Itemでは現在のitemを扱います。まずは全件を見渡しやすいAll Itemsで、入力と期待値をそろえましょう。
n8nでは、ノード間のデータをitemの配列として渡す構造が基本です。通常の項目はjson、添付ファイルなどの情報は別のbinaryに入り、jsonだけを作り直すと必要なファイル情報を落とすおそれがあります。
All Itemsで型を判定して返す
最初の作例は、次の4件です。元のamountを上書きせず、変換後の数値と分類を別の項目に追加します。
| id | amount入力 | amount_value | amount_kind | pairedItem.item |
|---|---|---|---|---|
| A | 文字列 “2” | 数値 2 | valid | 0 |
| B | キーなし | null | missing | 1 |
| C | 数値 0 | 数値 0 | valid | 2 |
| D | 文字列 “abc” | null | invalid | 3 |
- 外部接続のない検証用ワークフローを作る
本番を編集せず、新規または複製した未有効化のワークフローを用意してください。Manual Triggerの先にCodeを置き、名前を
Mock Items、言語をJavaScript、モードをAll Itemsに設定します。 - 4件の入力を用意する
次の入力作成コードをMock Itemsへ貼り付け、手動で動かす構成にしてください。Manual Triggerから1件の入力を受ける前提であり、各出力のpairedItemはその同じ入力0番を示す設定です。
- 加工用のCodeを追加する
Mock Itemsの先に別のCodeを接続し、こちらもJavaScriptとAll Itemsに合わせてください。入力作成コードとは分け、加工用のコードを入れます。
Mock Itemsに入れる入力作成コードは次のとおりです。データは説明用に作ったもので、顧客情報や実際の金額は使いません。
return [ {json: {id: 'A', amount: '2'}, pairedItem: {item: 0}}, {json: {id: 'B'}, pairedItem: {item: 0}}, {json: {id: 'C', amount: 0}, pairedItem: {item: 0}}, {json: {id: 'D', amount: 'abc'}, pairedItem: {item: 0}},];次に、加工用Codeへ以下を貼り付けます。normalizeAmountが値を分類し、mapが各itemの元フィールドを残しながら結果を追加する構成です。
function normalizeAmount(raw) { if (raw === null || raw === undefined ||(typeof raw === 'string' && raw.trim() === '')) { return {amount_value: null, amount_kind: 'missing'}; } const decimal = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/; const permitted = typeof raw === 'number' || (typeof raw === 'string' && decimal.test(raw.trim())); const value = permitted ? Number(raw) : NaN; return Number.isFinite(value) ? {amount_value: value, amount_kind: 'valid'} : {amount_value: null, amount_kind: 'invalid'};}return $input.all().map((item, index) => ({ ...item, json: {...item.json, ...normalizeAmount(item.json.amount)}, pairedItem: {item: index},}));コードを設定したら、Mock Itemsから加工用Codeまでを手動実行してください。期待値表を見ながら、出力JSONの4件と各項目の型を照合する段階です。
冒頭の判定で、キーなし・null・空文字・空白だけの文字列をmissingに分けています。それ以外は、数値そのものか、半角の符号と小数で表せる文字列だけを数値化する設計です。
このため、Number("")のような変換を最初から行い、欠損を0として扱う問題を避けられます。0は有効な数値なので、単にif (!raw)で欠損を判定する方法は使いません。
| 入力例 | 分類 | この作例での扱い |
|---|---|---|
| ” 2.5 “、”-3″、”.5″ | valid | 前後の空白を除いた小数文字列を受け付ける |
| null、undefined、””、”“ | missing | amount_valueはnull |
| true、配列、”1,000″、”¥100″、”1e3″ | invalid | 一般的な金額表記や指数表記の文字列は受け付けない |
| Infinity、NaN | invalid | 有限の数値に限定する |
この規則で扱うのは限られた数値入力です。たとえば文字列の"1e3"は不正としますが、すでに数値1000として入力された値は有効になるため、文字としての表記規則と数値の型を分けて考えてください。
売上や支払いの計算へ転用する場合は、桁数、丸め、小数誤差、許容範囲を別途設計する必要があります。この作例では、入力の種類を見分けるところまでを扱う範囲です。
返却部分の...itemはjson以外の属性も引き継ぎ、...item.jsonは元のidやamountを保持します。今回はファイルを使いませんが、必要なbinaryを落とさない形を保ちました。
新しく付けるamount_valueとamount_kindに同名の入力項目があれば、その2項目は上書きされます。実データへ広げる前に、項目名の衝突も調べましょう。
Each Itemへ変えるときは返却部分も書き換える
Each Itemでは、同じ変換関数を使っても、入力の取り方と返す形を変えます。モードだけを切り替え、All Items用の配列処理を残す使い方は避けてください。
比較する場合は加工用Codeを複製し、言語をJavaScript、モードをRun Once for Each Itemに設定します。次のコードなら、現在の$json.amountを分類し、1つのitemを返す形です。
function normalizeAmount(raw) { if (raw === null || raw === undefined ||(typeof raw === 'string' && raw.trim() === '')) { return {amount_value: null, amount_kind: 'missing'}; } const decimal = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/; const permitted = typeof raw === 'number' || (typeof raw === 'string' && decimal.test(raw.trim())); const value = permitted ? Number(raw) : NaN; return Number.isFinite(value) ? {amount_value: value, amount_kind: 'valid'} : {amount_value: null, amount_kind: 'invalid'};}const output = { json: {...$json, ...normalizeAmount($json.amount)},};if ($input.item.binary) { output.binary = $input.item.binary;}return output;関数定義の部分はAll Items版と同一で、末尾だけが異なります。$input.item.binaryがある場合のみ引き継ぎ、各itemのIDや値は元のjsonから残す設計です。
このような各itemの1対1変換では、n8nの自動対応付けを前提に、後段の参照結果を照合してください。全件をまとめて合計したい場合はAll Itemsなど処理全体を扱える方法へ戻し、各item用コードに全件集計を混ぜないことが大切です。
4件を両モードへ渡したとき、A・B・C・DのID、amount_value、amount_kindが一致することを確認します。同じ処理内容を二重に本番へ接続する必要はありません。
件数が変わっても元itemへの対応を残す
Codeの出力では、値に加えて対応情報も確認します。後段で前のノードの値を参照するなら、どの出力が、どの入力から生まれたかという対応も必要です。
pairedItemと業務IDを別々に確認する
id: "A"のような業務IDと、n8nのpairedItemは役割が異なります。業務IDはデータの中身、pairedItemはワークフローをさかのぼるための対応情報です。
All Items版では、新しく返すitemのpairedItem: {item: index}を設定しました。ここでのindexは、加工用Codeが受け取った元の入力位置を指し、配列の先頭は0番です。
たとえばvalidだけ残すと、出力はAとCの2件になります。このとき必要な対応は0と2であり、出力の並びが0番・1番になったからといって、pairedItemまで0と1へ振り直してはいけません。
All Items版の末尾を次の形へ変えると、対応情報を付けたあとで絞り込めます。上で定義したnormalizeAmount関数はそのまま残してください。
return $input.all() .map((item, index) => ({ ...item, json: {...item.json, ...normalizeAmount(item.json.amount)}, pairedItem: {item: index}, })) .filter(item => item.json.amount_kind === 'valid');後段に外部送信をしないEdit Fieldsを置き、現在の$json.idと、式{{ $("Mock Items").item.json.id }}を別々の項目へ出すと、対応の確認がしやすくなります。ノード名が違う場合は、その実名へ置き換えてください。
絞り込み後にAとA、CとCの組が得られることが期待値です。pairedItemの数値だけで済ませず、実際に参照している元のIDまで一致を確かめましょう。
既成の集約や配列展開が目的なら、AggregateやSplit Outに戻って設定を比較できます。Codeへ寄せることより、元データから出力まで説明できることを優先しましょう。
異常値を止めて、変更を安全に戻す
この作例の完了条件は、4件の出力が見えることだけではありません。正常値と異常値を区別し、ID・型・対応情報を確認したうえで、後続の書き込みへ進めるかを判断します。
不正値を正常値に混ぜず、再実行の範囲を決める
Aは数値2、Bはmissing、Cは数値0、Dはinvalidが期待値です。BとDはいずれもamount_valueがnullですが、理由が違うためamount_kindも一緒に残します。
後段で数値を使う場合は、amount_kindがvalidのitemだけを対象にする条件を置いてください。不正値が一つでもあれば全体を止めたい業務では、送信や書き込みより前に異常の有無を判定する構成が必要です。
初めは、invalidのDを0へ置き換えて処理を続けないことを確認しましょう。実際のゼロと不正な入力を同じ値にすると、後から区別できなくなるためです。
- 全件を残す版は4件、validに絞る版はAとCの2件になっている。
- 数値2と0は文字列へ変わらず、missing・invalidの値はnullである。
- 元のidとamountを保持し、Bには元からamountキーがないことを区別できる。
- AとCだけ残した場合、pairedItemの元入力位置は0と2である。
- 後段から参照したMock ItemsのIDと、現在のIDが一致している。
- 必要なbinaryがある場合は保持され、意図しない送信・更新を行っていない。
返却形のエラーなら、まずAll Itemsで配列を返しているか、Each Itemで1つのitemを返しているかを見直します。次に、jsonがオブジェクトになっているか、参照した項目が実際の入力にあるか、コードの終了経路で何も返していない箇所がないかを確認してください。
入力が0件の場合、このAll Items用の変換を空配列へ適用した結果は空配列です。ただし実ワークフローでは、前のノードが0件を出したためCode自体が実行されない場合もあり、出力0件だけを計算成功の証拠にはできません。
APIキーや実際の顧客情報をコード、サンプル、console.logへ埋め込まないでください。認証が必要な通信は資格情報に対応したノードで扱い、この練習では外部通信やファイル操作を追加しません。
変更を戻すときは、控えておいた旧コードとモードを、検証用の複製上で差し戻します。実行に失敗しても、下流の別ノードがすでに行った外部書き込みまで自動で取り消されるとは扱わないでください。
確認後に本番へ反映する段階では、再実行で同じデータを二重送信しないかを別途点検する必要があります。まずは外部接続のないまま、期待値と一致するところまでを終えましょう。
まとめ|入出力とitem対応を決め、小さな変換を確かめる
n8nのCodeは、実行モードと返却構造をそろえ、欠損・不正値・元itemへの対応を明示して使うのが基本です。一つの設定欄の式や専用ノードで読みやすく表せる処理は、そちらを先に検討します。
次は外部接続のない検証用ワークフローに4件の架空データを入れ、A=2、B=missing、C=0、D=invalidを照合してください。IDと型がそろったらAとCだけ残し、後段で元のIDを参照できるかまで確かめましょう。
コードの短さよりも、値が欠けた理由や入力との対応を追えることを重視するのが、後段の誤処理を見つけやすくする設計だと考えます。その説明ができる変換から、必要な処理を一つずつ追加する進め方が適切です。
参考資料:Expressions versus data nodes、Code node、データ構造、Codeでのitem対応の保持。