Closed4

Athena-Queryを使ってみる。

若槻龍太若槻龍太

Athena-Queryを使ってみる。

AWS SDKを生で使ってAthenaクエリをしようとするとポーリング処理を設ける必要があり面倒(Amazon Athenaのクエリ実行と実行結果の取得をAWS SDKでやってみた(exponential-backoff) | DevelopersIO)。Athena-Queryを使えば記述をシンプルにできる。

インストール

npm install @classmethod/athena-query @aws-sdk/client-athena

使う

import { Athena } from '@aws-sdk/client-athena';
import AthenaQuery from '@classmethod/athena-query';

const athena = new Athena({});
const athenaQuery = new AthenaQuery(athena, {
  db: 'default',
  workgroup: 'athenaWorkGroup',
  catalog: 'AwsDataCatalog',
});

const main = async () => {
  for await (const item of athenaQuery.query('SELECT * FROM device_table;')) {
    console.log(item); // You can get all items across pagination.
  }
};

main();

試行1

実行したらコンストラクタじゃないと言われる。

$ npx ts-node query.ts
/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/query.ts:5
const athenaQuery = new AthenaQuery(athena);
                    ^
TypeError: athena_query_1.default is not a constructor
    at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/query.ts:5:21)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at phase4 (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/node_modules/ts-node/src/bin.ts:649:14)
    at bootstrap (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/node_modules/ts-node/src/bin.ts:95:10)

AthenaQueryがdefault exportされていないから。

Another solution would be to add "esModuleInterop": true, into tsconfig.json.

esModuleInterop allows default imports from modules with no default export.

自分、以前にも同じ事象でハマっていた。

tsconfig.jsonのcompilerOptionsに"esModuleInterop": trueを設定したら、実行成功した。

$ npx ts-node query.ts
{ amount: 15, deviceid: 'device_01' }
{ amount: 1, deviceid: 'あああ' }

試行2

tsxを使えば、esModuleInteropを設定しなくても、CommonJSモジュールを実行できる。

tsx is a CLI command (alternative to node) for seamlessly running TypeScript & ESM, in both commonjs & module package types.

esModuleInteropを設定せずに実行できた。

$ npx tsx query.ts 
Need to install the following packages:
  tsx
Ok to proceed? (y) y
{ amount: 15, deviceid: 'device_01' }
{ amount: 1, deviceid: 'あああ' }
若槻龍太若槻龍太

AWS Lambda上で使ってみる

実装

  • lib/cdk-sample-app.queryDevicesFunc.ts
import { Athena } from '@aws-sdk/client-athena';
import AthenaQuery from '@classmethod/athena-query';

const athena = new Athena({});
const athenaQuery = new AthenaQuery(athena, {
  db: 'default',
  workgroup: 'primary',
  catalog: 'AwsDataCatalog',
});

export const handler = async () => {
  const items = [];

  for await (const item of athenaQuery.query('SELECT * FROM device_table;')) {
    items.push(item);
  }

  return items;
};

デプロイはCDKで行う。GlueやAthena周りのリソースは別途作成済み。権限付与は簡略化。

  • lib/cdk-sample-app.ts
import { Construct } from 'constructs';
import { aws_lambda_nodejs, aws_iam, Stack, StackProps } from 'aws-cdk-lib';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const queryDevicesFunc = new aws_lambda_nodejs.NodejsFunction(
      this,
      'queryDevicesFunc'
    );

    queryDevicesFunc.addToRolePolicy(
      new aws_iam.PolicyStatement({
        actions: ['athena:*', 'glue:*', 'athena:*'],
        resources: ['*'],
      })
    );
  }
}

デプロイ。

npx cdk deploy "*" --require-approval never
若槻龍太若槻龍太

動作確認

実行できた。

[
  {
    "amount": 1,
    "deviceid": "あああ"
  },
  {
    "amount": 15,
    "deviceid": "device_01"
  }
]
このスクラップは2023/01/31にクローズされました