私はn8nを「受け渡し・通知・スケジュール起動」のハブとしてだけ使い、判断や複雑な分岐、大量データの加工はPythonに逃がすと決めていました(いまは同じ役割をCloudflare Workerと手元のPythonに移しています。線引きの考え方は変わっていません)。理由は単純で、n8nはノードをつなぐほど視覚的に把握しやすくなる反面、条件分岐やループが増えると、どのノードで何が原因で止まったのかを追うのに時間がかかるからです。コード側に逃がせば、ログ出力やテストで原因を特定しやすくなります。
n8nに任せる処理とコードに逃がす処理の線引き
具体的には「収集→分類→通知」という流れのうち、収集と通知はn8nに残し、分類はPythonに渡しています。Web収集は外部APIへのリクエストやスクレイピング結果の受け取りで、n8nのHTTP RequestノードやWebhookノードで完結します。取得したデータをカテゴリ分けしたりスコアを付けたりする分類処理は、条件が増えるほどn8nのIFノードやSwitchノードが縦に伸びて見通しが悪くなるため、Pythonのスクリプトに渡しています。通知の文面組み立てと送信は再びn8nに戻します。
判断基準を表にすると次のようになります。
| 処理 | 任せる先 | 理由 |
|---|---|---|
| Webhookでのデータ受信 | n8n | 受け渡しだけなのでノード1つで完結する |
| キーの正規化・欠損チェック | n8n(軽微なら) | Functionノードの数行で足りる範囲 |
| 分類・スコアリング(条件分岐が多い) | Python | 条件が増えるとn8nのノードが追いにくくなる |
| 大量データのループ加工 | Python | n8n上でのループは件数が増えると処理時間と可視性が落ちる |
| 保存・通知の送信 | n8n | 外部サービスへの受け渡しに特化したノードがある |
この線引きはジドウカblogでも紹介されているように、n8nがノーコードで多くのサービス連携ノードを持つ点を生かしつつ、複雑な判断ロジックは別に切り出す考え方と重なります。
最初の1フローを組む:Webhook→整形→Supabase保存→LINE通知
最小構成はWebhookノードで生データを受け、Functionノードで整形し、Supabaseに保存し、保存結果に応じてLINEに通知するという4段階です。詰まりやすいのは整形と通知条件の部分なので、そこを手順で分解します。
- n8nでWebhookノードを作成し、Production URLをコピーする。ここで詰まりやすいのは、テスト実行中に表示されるTest URLを本番の送信先に設定してしまうケースです。Test URLは「Listen for test event」を押している間しか受け付けないため、必ずProduction URLに差し替えます。
- Webhookのメソッドを送信元の仕様に合わせる。POSTで送られてくるのにGETのままにしていると、何も受信できないまま気づかないことがあります。
- 受け取ったJSONのキー名を確認する。送信元によって日本語キーと英語キーが混在していることがあるため、Functionノードでキー名を統一します。
- Functionノードで欠損チェックを入れる。必須項目が空のレコードをそのままSupabaseに送ると、後工程のLINE通知で中身が空のメッセージが届くため、ここで弾きます。
- Supabaseノード(またはHTTP Request経由)で保存する。テーブルのカラム名とn8n側のJSONキーが一致していないと、エラーにならずに一部カラムだけnullで保存されることがあるため、保存後に実際のレコードを確認します。
- 保存が成功したレコードだけを対象にLINE通知を出す。保存前に通知を送ると、保存に失敗した場合でも「保存できた」という誤った通知が届くため、通知は保存ノードの後段に置きます。
整形のFunctionノードは、たとえば次のような内容になります。
// 受信したJSONのキーを正規化し、欠損があれば除外する
const items = $input.all();
const result = [];
for (const item of items) {
const data = item.json;
const title = data.title || data["タイトル"] || null;
const url = data.url || data["URL"] || null;
if (!title || !url) {
continue; // 必須項目が欠けているレコードは保存対象から外す
}
result.push({
json: {
title: title.trim(),
url: url.trim(),
received_at: new Date().toISOString()
}
});
}
return result;
通知の文面条件は、保存件数が0件のときは送らない、1件以上のときだけタイトルと件数をまとめて送る、といった単純な条件から始めると運用で崩れにくくなります。
n8nが向かない処理を自社運用ではどう逃がしているか
私は定時タスクの運用を、n8nではなくWindowsのタスクスケジューラとPythonの組み合わせで回しています。内容はWebの収集、AIでの分類と下書き作成、LINE webhookでの通知、Cloudflare Workerでの処理、Supabaseへの保存です。公開や送信の最終判断は人が行う設計にしています。
n8nではなくこの構成を選んでいる理由は、タスクの本数が増えるとn8nのワークフロー一覧を画面でスクロールして把握するコストが上がるためです。Pythonのスクリプトであればファイル名とフォルダ構成で分類でき、バージョン管理の仕組みにもそのまま乗せられます。AIでの分類のようにロジックが変わりやすい処理も、コードであれば差分で変更履歴を追えます。
逆に、WebhookやLINEへの通知送信のような「受け渡すだけ」の部分は、n8nに戻すとノード1つで完結するため、わざわざPythonで書く必要はないと判断しています。n8nに向く処理とコードに向く処理は、処理そのものの難しさよりも「分岐の数」と「変更の頻度」で見分けるのが実務上やりやすいというのが私の結論です。
止まる場所:認証切れ・再試行・通知の二重送信
私の運用では、公開や送信の最終判断を人が行うという一段を必ず挟むことで、認証切れや再試行の失敗がそのまま誤通知につながるのを防いでいます。自動化フローは「収集・選定・通知の下書きまで」を無人で進め、実際に外部へ公開・送信する直前で人の確認を挟む設計です。
認証切れについては、処理が失敗してからログで気づくのではなく、外部サービス側の仕様変更やトークン失効がそのまま次の工程の失敗として表面化する設計にしています。LINEやSupabaseのような外部サービスは、一時的な通信エラーと認証切れを区別しないとどちらに対しても同じ再試行をしてしまい、認証切れの場合はいくら再試行しても復帰しません。そのため、エラーの種類によって「再試行して待つ」か「人に知らせて止める」かを分けることが、通知が止まったまま気づかないという事故を防ぐ基本的な考え方になります。
通知の二重送信は、保存処理と通知処理を同じフロー内で順番に実行し、保存が成功したレコードだけを通知対象にすることで防いでいます。保存と通知を別々のフローに分けて並行で動かすと、片方が再試行している間にもう片方が別のタイミングで動いてしまい、同じ内容が二度届くことがあるため、1つのフロー内で順番を固定しておくのが安全です。
常時稼働は必要か:VPSに置く基準と置かない基準
常時稼働が必要かどうかは、処理が「決まった時刻に人が確認する前に動いている必要があるか」で判断しています。私が運用している日本株の監視ボード(funnel.gpirot.com)は、寄り前の板を機械が読んで候補を選び、結果を毎日採点して公開するところまでを無人で毎営業日動かしています。これは市場が開く前に候補を選び終えている必要があるため、ローカルPCの電源が入っているかどうかに依存しない常時稼働の仕組みが必要になります。
技術構成としては、サイトはCloudflare PagesとPages Functions、定時処理はWindowsのタスクスケジューラとPython、保存先はSupabase、通知はLINEという組み合わせで、公開や送信の最終判断は人が行っています。市場が開く前という決まった時刻に間に合わせる必要がある処理は、この構成の中でも常時動いている部分に乗せています。
一方で、複数の定時タスクのうち収集や下書き作成のように「人が後から確認するタイミングまでに終わっていればよい」処理は、ローカルPCの定時実行で足りています。常時稼働を増やすかどうかを考えるときは、処理内容の複雑さよりも「間に合わせるべき時刻があるかどうか」を先に確認すると、VPSを増やす前に判断がつきやすくなります。
次の一歩:自分のフローで最初に決めること
自分の案件でn8nとコードを仕分けるときは、次の項目を順番に確認すると判断が早くなります。
- 条件分岐が3つ以上になりそうか。なりそうならコードに逃がす候補にする。
- 処理対象の件数が増えたときにループが重くなるか。重くなりそうならコードに逃がす。
- 決まった時刻までに終わっている必要があるか。あれば常時稼働の仕組みを検討し、なければローカルの定時実行で足りるか確認する。
- 保存と通知が同じフロー内で順番に実行されているか。別フローに分けていないか確認する。
- 認証切れと一時的な通信エラーを同じ扱いで再試行していないか確認する。
この先に進むときは、常時稼働をVPSに置くかどうかの判断、保存先をSupabaseに統一する考え方、LINEでの人による承認フローの作り方といったテーマを別の記事でそれぞれ扱っているので、気になる項目から読み進めると整理しやすいはずです。まずは自分のフローで、上記の5項目をチェックするところから始めてみてください。

SNS投稿のAI下書きと人の承認を分ける仕組みの作り方
SupabaseをAI自動化のデータの出入り口に統一した実装と実測
n8nとLINEとSupabaseでスマホ承認フローを最後まで動かす実装手順