2025年7月31日木曜日

APEXアプリケーションからn8nのワークフローを呼び出す

n8nで定義したワークフローをOracle APEXのアプリケーションから呼び出してみます。また、ワークフローの処理に時間がかかる場合を想定して、n8nのワークフローよりOracle REST Data ServicesのREST APIを呼び出す手順を確認します。

n8nには色々なアプリケーションと連携するアクションが標準で含まれていますが、Oracle APEXやOracle Databaseと連携するアクションは見つかりませんでした。そのため、APEXアプリケーションからの呼び出しをWebhookで受け付け、HTTP RequestでORDSのREST APIを呼び出します。

以下のワークフローをn8nで作成しています。中間のCodeでは以下のJavaScriptのコードを実行し、受信したデータをそのまま出力に渡しています。

return $input.all();


APEXアプリからのn8nのWebhookの呼び出し、および、n8nのHTTP RequestでのORDS REST APIの呼び出しは、基本的にREST APIの呼び出しなので難しいことはありませんが、認証については少し工夫が必要です。本記事は、主に認証での工夫について説明しています。

以下より、実施した検証作業を紹介します。作業はApple SiliconのMacbook Proで実施しています。

Oracle APEXについては、こちらの記事「podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する」で紹介している手順で作成した環境を使用します。

n8nを動かすサーバーを準備します。n8n Community Editionをコンテナとして実行します。公式ドキュメントのDocker Installationに記載されている手順に沿って、作業を実施します。

最初にボリュームを作成します。作成したボリュームにすべてのデータを保存するため、ボリュームが維持されていれば、コンテナを作り直してもそれまでの作業は維持されるようです。

podman volume create n8n_data

% podman volume create n8n_data

n8n_data

% 


n8nを実行するコンテナをn8nという名前で作成します。

-e N8N_RUNNERS_ENABLED=true および -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true のオプションについては、指定しないと警告が表示されたので追加しました。

podman run -it --rm --name n8n -e N8N_RUNNERS_ENABLED=true -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

% podman run -it --rm --name n8n -e N8N_RUNNERS_ENABLED=true -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

No encryption key found - Auto-generating and saving to: /home/node/.n8n/config

Initializing n8n process

n8n ready on ::, port 5678

Migrations in progress, please do NOT stop the process.

Starting migration InitialMigration1588102412422

Finished migration InitialMigration1588102412422


[中略]



n8n Task Broker ready on 127.0.0.1, port 5679

[license SDK] Skipping renewal on init because renewal is not due yet or cert is not initialized

Registered runner "JS Task Runner" (IFWQndGFfYn8ZyDN5Bma5) 

Version: 1.104.1


Editor is now accessible via:

http://localhost:5678


Press "o" to open in Browser.



オプション -p 5678:5678 を指定して、コンテナのポート5678をホストのポート5678にマップしています。ホストのブラウザからは、以下のURLでn8nにアクセスします。

http://localhost:5678/

最初にオーナー・アカウントの設定が求められます。以降は会社の規模や職種、利用者の役職や利用目的などの入力を求められます。


今回の作業はAPEXアプリケーションからの呼び出しができれば良かったので、Get paid features for free (forever)についてはskipしました。

アカウントの設定が完了すると、n8nのコンソールが開きます。

ワークフローの作成はスクラッチから始めます。


最初のノードとしてWebhookを追加します。


Webhookの基本的な設定を行います。

HTTP MethodPOSTRespondWhen Last Node FinishesResponse DataAll Entriesとします。


AuthenticationをクリックするとBasic AuthHeader AuthJWT AuthNoneの4つの選択肢が表示されます。認証が簡単なのはBasic Authですが、今回はJWT Authを選びます。

Credential for JWT Authをクリックして、+ Create new credentialを実行します。


JWTの署名の生成に関わる情報を設定します。今回は、APEX側ではAPEX_JWT.ENCODEを呼び出してJWTを生成する予定です。そのため、署名アルゴリズム(Algorithm)として選択できるのはHS256のみになります。HS256はハッシュ・アルゴリズムなのでKey TypePassphraseになります。Secretパスフレーズである文字列を入力します。この文字列を秘密キーとして、Oracle APEXのアプリケーションと共有します。

クリデンシャルの名前をAPEX Authに変更し、Saveします。


以上で、APEXアプリからの呼び出しを受け付けるWebhookが設定できました。

Webhook URLをコピーし、キャンバスに戻ります。


Webhookの後続のノードとしてCode(JavaScriptの実行)を追加します。


ModeRun Once for All itemsLanguageJavaScriptJavaScriptに以下を記述します。

return $input.all();

以上の設定を行い、キャンバスに戻ります。


この時点で一旦ワークフローをSaveします。


Execute Workflowをクリックします。WebhookにWaiting for you to call the Test URLと表示されているように、Webhookの呼び出し待ちになります。


APEXでの作業に移り、このn8nのワークフローを呼び出すAPEXアプリケーションを作成します。

空のAPEXアプリケーションを作成します。名前Call n8n Workflowとします。


アプリケーションが作成されます。機能はすべてホーム・ページに作成します。

ホーム・ページページ・デザイナで開きます。


最初にボタンSUBMITを作成します。n8nのWebhookを呼び出し、ワークフローを開始するボタンです。

外観ホットオンテンプレート・オプションWidthStretchにします。動作アクションはデフォルトのページの送信です。


送信するデータを入力するページ・アイテムP1_REQUESTを作成します。タイプテキスト領域です。外観高さ15に拡張しておきます。


同様にn8nのワークフローからのレスポンスを表示するページ・アイテムP1_RESPONSEを作成します。P1_REQUESTの右隣に配置するため、レイアウト新規行の開始オフにします。


ボタンSUBMITをクリックしたときに実行されるプロセスを作成します。

プロセスの名前Call n8n Workflowとします。タイプコードを実行ソースPL/SQLコードとして以下を記述します。


サーバー側の条件ボタン押下時SUBMITを指定します。


コード中でWebhook URLG_WEBHOOK_URLシークレットG_SECRETから参照しています。これらはアプリケーション定義置換文字列として設定します。

APEXをpodmanのコンテナとして実行している場合は、Webhook URLのlocalhostの部分は、host.containers.internalに置き換える必要があります。

置換文字列G_WEBHOOK_URLとしてWebhookのURLG_SECRETとしてAPEX Auth作成時に設定したSecretの文字列を設定します。


シークレットのような秘密の値を置換文字列として設定するのは、安全な方法ではありません。実際に運用する場合は、Oracle CloudであればVaultなどにシークレットとして保存するなど、安全を確保するためにもう一工夫が必要です。

以上で、APEXアプリケーションからn8nのワークフローを開始できるようになりました。

n8nのワークフローがWaiting for triggering eventの状態であることを確認します。


RequestにJSONを入力し、ボタンSubmitをクリックします。ワークフローは入力をエコーバックするだけなので、正しいJSONであれば内容はなんでも構いません。

ワークフローが返すレスポンスが表示されます。内容を見ると、送信したリクエストの他にHTTPヘッダーやJWTに含まれるiss、sub、audoの値なども参照できることが確認できます。


n8nのコンソールではWorkflow executed successfullyと表示され、ワークフローが終了します。


以上で、APEXアプリからn8nのワークフローを開始できるようになりました。

次にn8nのワークフローからのコールバックを実装します。

最初にn8nからコールバックされた内容を保持する表N8N_CALLBACKを作成します。



n8nから呼び出すOracle REST Data ServicesのREST APIを作成します。

モジュールとしてn8n、テンプレートとしてcallback、POSTハンドラとして以下のコードを記述しています。
begin
    insert into n8n_callback(response) values(:body_text);
    commit;
end;


作成したREST APIをOAuth2で保護します。

コードを実行するとclient_idclient_secretが印刷されます。一度しか印刷されないので、コピーして保存します。この値をn8n側に設定します。


コピーを忘れてしまった場合は、スクリプトを再実行します。

APEXアプリケーションのホーム・ページに、表N8N_CALLBACKの内容を表示する対話モード・レポートを追加します。

識別名前n8n Callbackソース表名N8N_CALLBACKを設定します。


以上でAPEXアプリケーションの準備は完了です。

n8nのワークフローにHTTP Requestのノードを追加します。


HTTP Requestに、以下の設定を行います。

MethodPOSTURLはORDSのREST APIのURLを指定します。モジュール・パスが/n8n/、テンプレート名がcallbackとして作成しているため、以下の形式になります。

http://ホスト:ポート/ords/ワークスペース名/n8n/callback

今回の作業ではワークスペース名apexdev、n8nはコンテナで動作しているため、以下を設定しています。

http://host.containers.internal:8181/ords/apexdev/n8n/callback

AuthenticationGeneric Credential TypeGeneric Auth TypeOAuth2 APIを選択します。OAuth2 APIAPEX OAuth2 Credを設定していますが、この設定については、この後に紹介します。

Send Bodyオンにし、Body Content TypeJSONSpecify BodyUsing JSONJSON{{ $json.body }}を設定します。入力されたJSONドキュメントを、ORDS REST APIの呼び出しの本体としてそのまま返しています。


APEX OAuth2 Credとして、以下を設定しています。

Grant TypeClient Credentialsです。Access Token URLはORDSが標準で提供しているトークンURLで、以下の形式になります。

http://ホスト:ポート/ords/ワークスペース名/oauth/token

今回の作業ではワークスペース名apexdev、n8nはコンテナで動作しているため、以下を設定しています。

http://host.containers.internal:8181/ords/apexdev/oauth/token

Client IDおよびClient Secretは、APEX側でOAuth2による保護を行なうスクリプトを実行した時にclient_idおよびclient_secretとして印刷された値を設定します。

ScopeにはORDSのモジュールの保護に使用した権限名を設定します。今回はn8n.privを設定します。

ORDSのトークンURLは、client_idおよびclient_secretはAuthorizationヘッダーとして送信されることを期待しているため、AuthenticationにはHeaderを設定します。


以上で、n8nのワークフローの更新は完了です。

ワークフローを実行します。


改変したAPEXアプリケーションからワークフローを開始します。

Requestに以下を入力し、ボタンSubmitをクリックします。

{
    "name": "my first request"
}

ワークフローがすぐに終了するため、ページの再描画前にn8nのワークフローからのコールバックが完了するようです。

そのため、対話モード・レポートにRequestとして与えた文字列がすぐに表示されます。


今回の記事は以上になります。

簡単なアプリケーションですが、作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/call-n8n-workflow.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。