Codeノードは、ノードと式で書けない処理をJavaScriptかPythonで埋めるノードです。ここでは「問い合わせ受付」に電話番号の整形・重複の判定・本文からの抜き出しを足しながら、2つの実行モード、JavaScriptとPythonの違い、セルフホストで外部ライブラリを使う設定、やってはいけないことまでを書きます。前提は「n8nの使い方入門:最初のワークフローを作って動かす」の問い合わせ受付です。版は2026年10月時点の安定版 2.44.7 で確かめています。
先に決めること
Codeで書くか、ノードと式で済ませるか
Codeノードは最後の手段です。式と違ってプレビューが出ず、値は動かして確かめるしかありません。公式も式で書けるところは式でと勧めています。それに、直せる人が限られます。実際に本番で動かしていると、Codeは書いた人が異動したあと誰も触れなくなり、止まったときに調べが一番遅れる場所になります。
1つの値を整えるなら式、項目を足すならEdit Fields (Set)、件数を絞る・まとめるなら専用のノードを使い、ノードを3つも4つも重ねないと書けないときだけCodeにします。式の書き方は「n8nの式(Expression)とデータの扱い:$json・ノードの参照・日付・JSON」、条件で分ける話は「n8nの分岐とループ:IF・Switch・Merge・Loop Over Items」に分けています。
JavaScriptか、Pythonか
迷ったらJavaScriptです。2.44.7のPythonは、n8nの組み込み変数が入力の _items と _item しか使えず、標準ライブラリの re(正規表現)も既定ではimportできません。Pythonを選ぶのは、保守する人がPythonしか書けないときと、セルフホストでpandasなどを入れて使うときです。
どこで動かしているか
2.xのCodeノードは、n8n本体とは別の「task runner」(コードを隔離して動かす仕組み)の上で動きます。本体の子プロセスで動かすのが内部モード、別のコンテナで動かすのが外部モードで、Cloud・手元のPC・セルフホストで使える範囲が違います。「n8nをセルフホストで導入する:Docker ComposeでVPSに立て、独自ドメインとHTTPSで使う」の構成は外部モードで、task-runners コンテナが n8nio/runners のイメージで動いています。
使い分けと設定値の一覧
Codeの前に試すノードです。
| やりたいこと | 先に試すもの | Codeが要るとき |
|---|---|---|
| 項目を足す・名前を変える | Edit Fields (Set) | ほぼ要らない |
| 空白を落とす、小文字にそろえる | 式(.trim() など) | 同じ整形を何項目にもかける |
| 条件に合う件数だけ残す | Filter | 条件が式で読めないほど多い |
| 前に処理した問い合わせを除く | Remove Duplicates | 「24時間以内」のように期間で区切る |
| 配列を分ける・まとめる | Split Out・Aggregate | 分け方が中身で変わる |
| ハッシュや署名の計算 | Crypto | 計算した値をさらに比べる |
| 外部のAPIを呼ぶ | HTTP Request | Codeでは呼べない |
2つの実行モードの違いです。
| Run Once for All Items | Run Once for Each Item | |
|---|---|---|
| 動く回数 | 入力が何件でも1回(既定) | 入力1件ごとに1回 |
| 入力の取り方(JavaScript) | $input.all() | $input.item、$json |
| 入力の取り方(Python) | _items | _item |
| 返すもの | itemの配列 | item 1つ |
| 件数を変えられるか | 変えられる | 1件から1件 |
| 向いている処理 | 重複判定、集計、並べ替え | 整形、本文からの抜き出し |
動かす場所ごとの違いです。
| JavaScript | Python | |
|---|---|---|
| n8n Cloud | 使える。crypto と moment だけ読み込める | 使える。importは一切できない |
手元の npx n8n など内部モード | 使える。モジュールは既定で読み込めない | 公式は外部モードだけで使える扱い。npxでは「Python runner unavailable」で止まる |
| セルフホストの外部モード | 既定で crypto と moment を許可済み。ほかは設定で足す | 既定でimport不可。標準ライブラリは設定で、外部パッケージはイメージを足して使う |
| 使える組み込み変数 | $input $json $('ノード名') $now DateTime など | _items _item だけ |
セルフホストで触る設定です。置き場所を間違えると効きません。
| 設定 | 既定 | 置き場所(外部モード) |
|---|---|---|
NODE_FUNCTION_ALLOW_BUILTIN(Node.jsの組み込み) | crypto | runnersの設定ファイル |
NODE_FUNCTION_ALLOW_EXTERNAL(npm) | moment | runnersの設定ファイル |
N8N_RUNNERS_STDLIB_ALLOW(Python標準) | 空 | runnersの設定ファイル |
N8N_RUNNERS_EXTERNAL_ALLOW(Python外部) | 空 | runnersの設定ファイル |
N8N_RUNNERS_TASK_TIMEOUT(実行時間の上限) | 300秒 | n8n本体 |
手順
Codeノードを置いて、雛形を読む
- 問い合わせ受付の「問い合わせフォーム」と「問い合わせ一覧に追加」の間の線にカーソルを合わせ、+ を押す
- ノードパネルの検索欄に
codeと打ち、「Code」を選ぶ - 並んだ「Code in JavaScript」「Code in Python」のうち、「Code in JavaScript」を選ぶ
- 開いた詳細画面で、「Mode」が「Run Once for All Items」、「Language」が「JavaScript」になっていることを確かめる

最初から入っている雛形は、All Itemsのモードだと次のとおりです。
// Loop over input items and add a new field called 'myNewField' to the JSON of each one
for (const item of $input.all()) {
item.json.myNewField = 1;
}
return $input.all();
$input.all() が届いたitemの配列です。「Mode」を「Run Once for Each Item」に替えると、雛形は1件だけを扱う形に変わります。
// Add a new field called 'myNewField' to the JSON of the item
$input.item.json.myNewField = 1;
return $input.item;
返す形は、All Itemsなら [{ json: { ... } }] の配列、Each Itemなら { json: { ... } } を1つです。json で包まずに返してもn8nが包み直しますが、json と別の項目を同じ階層に混ぜると「Invalid output format」で止まります。

試すときは「Execute step」を押します。前のノードも一緒に動くので、一度フォームから送ったら「問い合わせフォーム」のOUTPUTで「Pin data」を押して固定します。Codeだけを何度でも試せ、固定したデータは本番の実行には使われません。
例1:電話番号の表記をそろえる(Each Item)
電話番号は全角、+81 始まり、かっこ付きとばらばらに届くので、検索できる形にそろえます。
- 「問い合わせフォーム」に「電話番号」の欄を足し、項目名を
phoneにする - Codeノードの名前を「電話番号を整える」に変える
- 「Mode」を「Run Once for Each Item」にする
- コード欄を次に置き換える
// 全角を半角にし、+81 を 0 に戻してから数字だけ残す
let tel = String($json.phone ?? '').normalize('NFKC');
tel = tel.replace(/^\+81\s*(\(0\))?/, '0').replace(/\D/g, '');
// 携帯とIP電話だけはハイフンを入れる
let formatted = tel;
if (/^(050|070|080|090)\d{8}$/.test(tel)) {
formatted = `${tel.slice(0, 3)}-${tel.slice(3, 7)}-${tel.slice(7)}`;
}
$input.item.json.phoneNormalized = formatted;
$input.item.json.phoneValid = /^0\d{9,10}$/.test(tel);
return $input.item;
090-1234-5678 と +81 90 1234 5678 はどちらも 090-1234-5678 に、03 (1234) 5678 は 0312345678 になります。固定電話にハイフンを入れないのは、市外局番の桁数が地域で違い、正規表現では正しく切れないからです。スプレッドシートの電話番号の列には {{ $json.phoneNormalized }} を入れます。

同じ処理をPythonで書くとこうなります。「Language」を「Python」に替えます。re が使えないので、文字の置き換えで組みます。
raw = str(_item["json"].get("phone") or "")
tel = raw.translate(str.maketrans("0123456789+", "0123456789+"))
digits = "".join(ch for ch in tel if ch in "0123456789")
if tel.strip().startswith("+81"):
digits = "0" + digits[2:]
if digits.startswith("00"):
digits = digits[1:]
formatted = digits
if len(digits) == 11 and digits[:3] in ("050", "070", "080", "090"):
formatted = digits[:3] + "-" + digits[3:7] + "-" + digits[7:]
_item["json"]["phoneNormalized"] = formatted
_item["json"]["phoneValid"] = len(digits) in (10, 11) and digits.startswith("0")
return _item
値は _item["json"]["phone"] と角かっこで取ります。_item.json.phone では動きません。

例2:同じ人からの二重送信を見分ける(All Items)
送信ボタンの2度押しや、翌朝の送り直しで二重に対応しないよう、24時間以内に同じメールアドレスから来ていたら印を付けます。Remove Duplicatesは「前に見た値か」で除くので、半年ぶりに来たお客さままで消えます。期間で区切るためにCodeを使います。
- 「問い合わせフォーム」の後ろにGoogle Sheetsノードを足し、名前を「問い合わせ一覧を読む」、Operationを「Get Row(s)」、Documentを「問い合わせ一覧」にする
- 「Settings」タブで「Always Output Data」をオンにする
- その後ろにCodeノードを足し、名前を「重複を判定」、Modeを「Run Once for All Items」にする
- コード欄を次に置き換える
- 後ろにIFノードを置き、
{{ $json.duplicate }}が true のときはSlackに「重複の可能性」と知らせる側へ分ける
2番は、スプレッドシートが空のときにCodeが動かなくなるのを防ぐためです。代わりに空のitemが1件流れるので、Codeで除きます。
// 今回の問い合わせ(フォームの値)
const current = $('問い合わせフォーム').first().json;
const email = String(current.email ?? '').trim().toLowerCase();
const receivedAt = DateTime.fromISO(current.submittedAt);
// スプレッドシートの既存の行(空のitemは除く)
const rows = $input.all()
.map(i => i.json)
.filter(r => Object.keys(r).length > 0);
// 同じメールアドレスで、24時間以内に受け付けた行を探す
const hit = rows.find(r => {
if (String(r['メールアドレス'] ?? '').trim().toLowerCase() !== email) return false;
const at = DateTime.fromFormat(String(r['受付日時'] ?? ''), 'yyyy-MM-dd HH:mm', { zone: 'Asia/Tokyo' });
return at.isValid && receivedAt.diff(at, 'hours').hours < 24;
});
return [{
json: {
...current,
duplicate: Boolean(hit),
duplicateRow: hit ? hit.row_number : null,
},
pairedItem: 0,
}];
入力は全行、出力は1件と件数が変わるのでAll Itemsで書きます。最後の pairedItem: 0 は、出力したitemがどの入力から来たかを伝える印です。件数を変えるCodeでこれがないと、後ろの $('問い合わせフォーム').item が「Paired item data … is unavailable」で止まります。受付日時の列は入門記事どおり yyyy-MM-dd HH:mm の日本時間で書いている前提で、書式を変えたら fromFormat も合わせます。


行が数千件を超えたら、Get Row(s)の「Filters」でメールアドレスを絞ってから渡します。ほかの重複の防ぎ方は「n8nとGoogleスプレッドシート連携:読み書き・更新・重複を防ぐ」にあります。
例3:本文から見積番号と人数を抜き出す(Each Item)
本文に書かれた見積番号(例: Q-202601)や利用人数を、一覧の列に抜き出します。
- 「電話番号を整える」の後ろにCodeノードを足し、名前を「本文から抜き出す」にする
- Modeを「Run Once for Each Item」にする
- コード欄を次に置き換える
- スプレッドシートに「見積番号」「人数」の列を足し、
{{ $json.quoteNo }}と{{ $json.headcount }}を入れる
// 全角を半角にしてから探す
const text = String($json.message ?? '').normalize('NFKC');
const quote = text.match(/Q-\d{6}/);
const people = text.match(/(\d{1,5})\s*(名|人)/);
const urls = text.match(/https?:\/\/[^\s<>"()]+/g) ?? [];
$input.item.json.quoteNo = quote ? quote[0] : '';
$input.item.json.headcount = people ? Number(people[1]) : null;
$input.item.json.urls = urls.join('\n');
return $input.item;
「先日いただいたQ-202601の見積について、社員50名で利用予定です」なら、quoteNo に Q-202601、headcount に 50 が入ります。日本語の問い合わせは全角が混ざるので、先に normalize('NFKC') で半角にしないと取りこぼします。

書いたコードの確かめ方
途中の値は console.log()(Pythonは print())で出します。出力先はブラウザの開発者ツールのコンソールです。
外部ライブラリを使う(セルフホスト)
セルフホストの外部モード構成が前提です。許可は task-runners コンテナの中の /etc/n8n-task-runners.json の env-overrides に書きます。公式もこのファイルに書くよう明記していて、n8n本体の environment に足しても効きません。
標準ライブラリだけ許可する(イメージはそのまま)
Pythonで re や datetime を使いたいだけなら、設定ファイルを差し替えるだけで済みます。
- compose.yaml がある作業フォルダで、既定の設定ファイルを取り出す
docker run --rm --entrypoint cat n8nio/runners:2.44.7 /etc/n8n-task-runners.json > n8n-task-runners.json
chmod 644 n8n-task-runners.json
n8n-task-runners.jsonの Python側のenv-overridesを書き換える
"env-overrides": {
"N8N_RUNNERS_STDLIB_ALLOW": "re,datetime,unicodedata,json",
"N8N_RUNNERS_EXTERNAL_ALLOW": ""
}
- compose.yaml の
task-runnersにファイルを差し込む行を足す
task-runners:
image: n8nio/runners:2.44.7
restart: always
volumes:
- ./n8n-task-runners.json:/etc/n8n-task-runners.json:ro
sudo docker compose up -dで作り直す
取り出したファイルは env-overrides の中だけを書き換えます。command や args まで消すとランナーが起動しません。
npmやPyPIのパッケージを足す(イメージを作る)
外部パッケージは、runnersのイメージに入れないと使えません。例はJavaScriptの電話番号ライブラリ libphonenumber-js と、Pythonの pandas です。
- 作業フォルダに
runnersフォルダを作り、上で取り出したn8n-task-runners.jsonを入れる - JavaScript側の
env-overridesを次のように書き換える
"env-overrides": {
"NODE_FUNCTION_ALLOW_BUILTIN": "crypto",
"NODE_FUNCTION_ALLOW_EXTERNAL": "moment,libphonenumber-js",
"N8N_RUNNERS_HEALTH_CHECK_SERVER_HOST": "0.0.0.0"
}
- Python側は
"N8N_RUNNERS_EXTERNAL_ALLOW": "pandas"にし、"N8N_RUNNERS_ALLOW_TRANSITIVE_IMPORTS": "true"を足す(pandasが中で読み込む別のパッケージまで並べずに済む) runners/Dockerfileを作る
ARG N8N_VERSION=2.44.7
FROM n8nio/runners:${N8N_VERSION}
USER root
RUN cd /opt/runners/task-runner-javascript && pnpm add libphonenumber-js
RUN cd /opt/runners/task-runner-python && uv pip install pandas
COPY n8n-task-runners.json /etc/n8n-task-runners.json
USER runner
- compose.yaml の
task-runnersのimageの行を、次の2行に替える
build: ./runners
image: n8n-runners-custom:2.44.7
sudo docker compose build task-runnersを実行し、続けてsudo docker compose up -d
Codeノードでは require で読み込みます。import 文は使えません。
const { parsePhoneNumberFromString } = require('libphonenumber-js');
const n = parsePhoneNumberFromString(String($json.phone ?? '').normalize('NFKC'), 'JP');
$input.item.json.phoneNormalized = n && n.isValid() ? n.formatNational() : '';
return $input.item;
固定電話も 03-1234-5678 や 0120-123-456 と正しく区切れます。
runnersの版はn8n本体と同じでなければいけないので、本体を上げたら ARG N8N_VERSION と image の行の版も同じ番号にして作り直します。アップデートの手順は「n8nを止めずに運用する:アップデート・バックアップ・エラー通知・脆弱性」にあります。
やってはいけないこと
APIキーやパスワードをコードに書く
Codeの中身はワークフローを開ける人なら誰でも読め、書き出したJSONにも入ります。CodeはCredential(認証情報)を読めない作りなので、認証が要る呼び出しはHTTP Requestに任せます。Credentialの扱いは「n8nの認証情報(Credential):OAuth2・APIキー・共有と権限」にあります。
環境変数を読もうとする
$env.SLACK_TOKEN のような読み出しは2.0から既定で止まり、「access to env vars denied」になります。Pythonの os.environ も別の設定 N8N_BLOCK_RUNNER_ENV_ACCESS(runnersの設定ファイルの env-overrides 側)で止まっています。$env は N8N_BLOCK_ENV_ACCESS_IN_NODE=false で戻せますが、どちらも編集できる人全員に環境変数を見せるので戻しません。設定値の共有は「Variables」($vars、CloudはPro以上・セルフホストはBusiness以上)を使います。
重い処理を載せる・安全対策を外す
Code 1回の実行は、既定で300秒を超えると「Task execution timed out after 300 seconds」で打ち切られ、3.0ではこれが60秒に縮む予告です。大きな集計はn8nの外で済ませるか、「Loop Over Items」で小分けにします。N8N_RUNNERS_INSECURE_MODE=true でランナーの安全対策を外すのと、内部モードのまま本番を回すのはやめます。公式は、内部モードだと編集できる人がDB・暗号鍵・Credentialまで読めるおそれがあるとしています。
応用
実行をメールアドレスで探せるようにする
$execution.customData.set('email', $json.email) と書くと、実行一覧をその値で絞り込めます。n8n CloudはPro以上、セルフホストは無料登録したCommunity以上で使えます。前回の実行の値を残す $getWorkflowStaticData は、テスト実行では保存されない実験的な機能なので、新しく作るなら「Data tables」に書きます。
振り分けはAIに任せる
「急ぎかどうか」「お見積もりかサポートか」のように書き方が決まっていないものは、正規表現では拾えません。「Text Classifier」や「Information Extractor」のノードを使います。つなぎ方は「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」にあります。
できないことと、その代わり
| できないこと | 代わりの手 |
|---|---|
CodeからHTTP通信をする(fetch も使えない) | HTTP Requestノードで呼び、結果をCodeで加工する |
| Codeからファイルを読み書きする | ファイルを読み書きするノードを使う |
| CodeでCredentialを読む | Credentialを付けたHTTP Requestに分ける |
| n8n Cloudで外部パッケージを読み込む | 処理を外のAPIに切り出すか、セルフホストにする |
Pythonで $('ノード名') や $now を使う | JavaScriptで書くか、前のEdit Fields (Set)で値をそろえる |
Pythonで class の定義、type()、open() を使う | 既定で禁止。関数と辞書で書く |
Codeの中で $evaluateExpression() を使う | 前のEdit Fields (Set)で式を評価して渡す |
| セルフホストでCodeの「Ask AI」タブを使う | Cloudだけの機能で、3.0で外れる予告もある。自分で書く |
よく詰まるところ
| 症状 | 原因 | 直し方 |
|---|---|---|
| 「Code doesn’t return items properly」 | All Itemsで配列ではなく値やオブジェクトを返した | [{ json: { ... } }] の配列で返す |
| 「Code doesn’t return a single object」 | Each Itemで配列を返した | 1件だけ返すか、Modeを「Run Once for All Items」にする |
| 「A ‘json’ property isn’t an object」 | json に配列や文字列を入れた | json には必ずオブジェクトを入れる |
| 「Module ‘…’ is disallowed」 | 許可の一覧に無い | runnersの設定ファイルの env-overrides に足す |
| 「Cannot find module ‘…’」 | イメージに入っていない | runnersのイメージを作り直す |
| Pythonで「Import of standard library module ‘re’ is disallowed」 | Pythonは既定でimportが止まっている | N8N_RUNNERS_STDLIB_ALLOW に足す |
| 「Python runner unavailable」 | npx n8n など内部モードで動かしている | 外部モードで立てるか、JavaScriptで書く |
| 「access to env vars denied」 | $env を読もうとした | Credentialか $vars に替える |
| 後ろの式で「Paired item data … is unavailable」 | 件数を変えたCodeに pairedItem が無い | 返すitemに pairedItem を付ける |
| スプレッドシートが空だとCodeが動かない | 前のノードの出力が0件 | 前のノードの「Always Output Data」をオンにする |
Pythonで 'dict' object has no attribute 'json' | _item.json と書いた | _item["json"] と書く |
| Codeの中の日時が13〜14時間ずれる | task-runners にタイムゾーンが無い | GENERIC_TIMEZONE を足す(JavaScriptの $now・Luxonに効く。TZ は設定ファイルの allowed-env に足さないと渡らない) |
次にやること
- n8nの式(Expression)とデータの扱い:$json・ノードの参照・日付・JSON
- n8nの分岐とループ:IF・Switch・Merge・Loop Over Items
- n8nをセルフホストで導入する:Docker ComposeでVPSに立て、独自ドメインとHTTPSで使う
- n8nを止めずに運用する:アップデート・バックアップ・エラー通知・脆弱性