
Code NodeワークフローでAthena接続用の外部パッケージをn8nに追加する
ノーコード・ローコードツールの「n8n」は、様々なWebサービスやアプリケーションを連携させ、ワークフローを自動化するための強力なプラットフォームです。その中でも「Code Node」は、標準で用意されているノードだけでは実現できない複雑な処理や、特定のサービスとの連携をJavaScriptコードを記述することで可能にする、非常にパワフルな機能です。
しかし、デフォルトのCode Nodeでは、n8nに組み込まれていない外部のNode.jsライブラリ(npmパッケージ)を直接利用することはできません。
この記事では、データ分析基盤として広く利用されている「Amazon Athena」を例に、n8nのセルフホスト環境で外部のnpmパッケージを追加し、Code NodeからAthenaに接続してデータを取得してみます。
ちなみに、標準のノードではまだAthenaはありませんでした。
n8nの構成
前提環境
n8nですが、AWSにセルフホストされた環境を利用します。
Docker Composeを使って起動しています。
n8n公式のドキュメント を参考にし作成しています。
例)
environmentにカスタム環境変数を設定できます
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
volumes:
- ./n8n_data:/home/node/.n8n
Node.jsライブラリのインストール
n8nのDocker Imageを使い、athenaのNode.jsライブラリをインストールし、それをビルドします。
Dockerfileに以下を追加して保存します。
FROM n8nio/n8n:latest
USER root
RUN npm install -g @aws-sdk/client-athena
USER node
保存後、Docker buildコマンドを実行し、カスタムイメージを作成します。
$ docker build -t n8n-custom-v1.0.1 .
docker images
コマンドで作成されたイメージが表示されるはずです。
この作成したイメージを起動することになります。
外部のモジュールを有効にする設定
セキュリティ上の理由から、Codeノードはモジュールのインポートを制限しています。
以下の環境変数を設定することで、組み込みモジュールと外部モジュールの制限を解除することができます。
export NODE_FUNCTION_ALLOW_EXTERNAL=@aws-sdk/client-athena
Docker Composeを更新
起動するイメージと、NODE_FUNCTION_ALLOW_EXTERNALの環境変数をdocker-compose.ymlに追加します。
n8n:
#image: n8nio/n8n:latest
image: {{ビルドしたDocker Image名}}
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- DB_TYPE=postgresdb
〜〜〜〜〜
略
〜〜〜〜〜
- NODE_FUNCTION_ALLOW_EXTERNAL=@aws-sdk/client-athena
volumes:
- n8n_storage:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
imageには前の手順でビルドしたイメージ名に変更します。
environmentにNODE_FUNCTION_ALLOW_EXTERNAL=@aws-sdk/client-athena を追加してインストールしたモジュールを使用できるようにします。
コンテナ起動
docker-compose.ymalのあるディレクトリで、
$ docker-compose up -d
を実行します。
これでn8nのコンテナが起動し、Athenaのライブラリが実行できる準備ができました。
Athena接続ワークフローの作成
それでは、実際にAthenaに接続し、SQLクエリを実行して結果を取得するワークフローを作成していきましょう。
今回のユースケースですが、S3に保存されたALBのアクセスログの分析 にします。
IAMロールに必要な権限を追加
n8nが使用しているEC2インスタンスのIAMロールに、Athenaの実行に必要な権限を追加する必要があります。
- データソースのS3バケットに対する読み取り権限 (s3:GetObject, s3:ListBucket)
- クエリ結果を出力するS3バケットに対する読み書き権限 (s3:PutObjectなど)
- Athenaサービス自体を操作する権限 (athena:StartQueryExecutionなど)
- Glueデータカタログ(テーブル定義など)を参照する権限 (glue:GetTableなど)
以下のようなポリシーをIAMロールに追加します。
(ALBのアクセスログをクエリするためのポリシー例)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAthenaDataSourceRead",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<<your alb log path>>",
"arn:aws:s3:::<<your alb log path>>/*"
]
},
{
"Sid": "AllowAthenaQueryResultWrite",
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::<your-athena-query-results-bucket>",
"arn:aws:s3:::<your-athena-query-results-bucket>/*"
]
},
{
"Sid": "AllowAthenaAndGlueAPI",
"Effect": "Allow",
"Action": [
"athena:StartQueryExecution",
"athena:GetQueryExecution",
"athena:GetQueryResults",
"athena:StopQueryExecution",
"glue:GetTable",
"glue:GetDatabase",
"glue:GetPartitions"
],
"Resource": "*"
}
]
}
Codeノードの追加
n8nのキャンバスで、+ボタンをクリックします。
ノード検索ウィンドウで「Code」と入力し、Code Nodeをワークフローに追加します。
JavaScriptコードの記述
Code Nodeを開き、以下のサンプルコードを貼り付けます
設定項目の部分は自身の環境に合わせて変更します。
// AWS SDK for JavaScript v3 のAthenaクライアントをインポート
const {
AthenaClient,
StartQueryExecutionCommand,
GetQueryExecutionCommand,
GetQueryResultsCommand
} = require("@aws-sdk/client-athena");
// --- 設定項目 ---
const AWS_REGION = 'ap-northeast-1'; // 例: 'ap-northeast-1' (東京リージョン)
const ATHENA_DATABASE = '<<your athena database name>>'; // Athenaのデータベース名
const ATHENA_OUTPUT_LOCATION = 's3://<<S3 path to output query results>>'; // クエリ結果の出力先S3パス
// 実行したいSQLクエリ
const SQL_QUERY = 'SELECT * FROM "<<your athena table>>" LIMIT 10;';
// --- 設定項目ここまで ---
// 非同期処理を実行するためのメイン関数
// n8nの実行コンテキスト(this)を引き継ぐために .call(this) で呼び出します。
async function main() {
try {
// Athenaクライアントの初期化
const athenaClient = new AthenaClient({
region: AWS_REGION
});
// 2. クエリ実行を開始
const startQueryExecutionCommand = new StartQueryExecutionCommand({
QueryString: SQL_QUERY,
QueryExecutionContext: {
Database: ATHENA_DATABASE,
},
ResultConfiguration: {
OutputLocation: ATHENA_OUTPUT_LOCATION,
},
});
const { QueryExecutionId } = await athenaClient.send(startQueryExecutionCommand);
console.log(`Query Execution ID: ${QueryExecutionId}`);
if (!QueryExecutionId) {
throw new Error('Query Execution ID not returned.');
}
// 3. クエリの実行完了をポーリングで待機
let queryState = '';
do {
// 3秒待機
await new Promise(resolve => setTimeout(resolve, 3000));
const getQueryExecutionCommand = new GetQueryExecutionCommand({ QueryExecutionId });
const queryExecutionResponse = await athenaClient.send(getQueryExecutionCommand);
queryState = queryExecutionResponse.QueryExecution.Status.State;
console.log(`Current query state: ${queryState}`);
if (['FAILED', 'CANCELLED'].includes(queryState)) {
throw new Error(`Query ${queryState}. Reason: ${queryExecutionResponse.QueryExecution.Status.StateChangeReason}`);
}
} while (queryState !== 'SUCCEEDED');
// 4. クエリ結果を取得
const getQueryResultsCommand = new GetQueryResultsCommand({ QueryExecutionId });
const queryResults = await athenaClient.send(getQueryResultsCommand);
// 5. 結果をn8nが扱いやすいJSON形式に整形
const resultSet = queryResults.ResultSet;
const headers = resultSet.Rows[0].Data.map(header => header.VarCharValue);
const rows = resultSet.Rows.slice(1).map(row => {
const rowData = {};
row.Data.forEach((item, index) => {
rowData[headers[index]] = item.VarCharValue;
});
return rowData;
});
// n8nの出力形式に合わせてデータを返す
return [{ json: { data: rows } }];
} catch (error) {
console.error(error);
// エラーが発生した場合もn8nに情報を返す
return [{ json: { error: error.message, stack: error.stack } }];
}
}
return main.call(this);
- ライブラリのインポート: require("@aws-sdk/client-athena") で、環境変数で許可したパッケージを読み込んでいます
- 結果の整形: Athenaから返されるデータは独特の形式をしているため、ヘッダー行とデータ行をパースし、n8nの後続ノードで扱いやすいJSONオブジェクトの配列に変換しています。
- n8nへの返却: Code Nodeは、returnされたデータを次のノードに渡します。
[{json: { ... }}]
というn8n標準のデータ構造で返すのが一般的です。
実行と結果の確認
実行するSQLですが、以下の内容にしています。
SELECT substr(time, 1, 10) AS req_date, count(request_url) as cnt
FROM "hermes"."alb_access_logs"
Where time between '2025-07-01' and '2025-07-31'
GROUP BY substr(time, 1, 10), substr(time, 1, 10)
order by req_date asc
;
2025-07のアクセス数を日別で表示するクエリです。
これをcodeノードに設定したJavascriptのコードの実行したいSQLクエリの箇所に貼り付けて実行してみます。
実行は、Execute step
というボタンを押します。
上記のようにAthenaでのクエリ結果が追加したJavascriptでの返却内容になっていることが確認できれば成功です。
これで、Athenaから取得したデータを、後続のGoogle Sheetsノードに書き出したり、Slackに通知したりと、自由に活用できるようになります。
まとめと課題
今回は、n8nのセルフホスト環境で環境変数を設定し、Code Nodeから外部のnpmパッケージ(@aws-sdk/client-athena)を利用する方法を使ってAthenaでクエリを実行してみました。
この方法を応用すれば、Athenaだけでなく、標準ノードが提供されていない様々なサービスや、特定の処理に特化したライブラリをn8nワークフローに組み込むことが可能になります。
Codeノードと外部パッケージの活用は、n8nの自動化ワークフローをより柔軟で強力なものになりますね。
Athenaでのクエリ実行で感じた課題もありました。
今回はユーザーは私1人として行っていましたが、n8nをチームや組織で利用する場合では、Athenaでクエリをして分析する条件や環境が違ってきます。
Iamロールにポリシーを追加してしまうと、n8nのユーザーなら誰でもアクセスできてしまうのでセキュリティ的にも管理的にも問題が出てくるでしょう。
n8nではユーザーごとにクレデンシャルを保存することができ、ノードでそれらを利用することも可能で一般的なやり方なのですが、Codeノードからはこのクレデンシャルにアクセスすることができませんでした(2025-08時点)。
代替案としては、Lambdaのノードを利用する が思いつきます。
Codeノードに書いたJavascriptの内容をLambdaに書いて,n8nから実行する で同じようなことができるのでこれも試してみたいですね。
公式からAthenaのノードがリリースされることも期待。