Closed4
Athena-Queryを使ってみる。
ピン留めされたアイテム
DevIOでブログ化した。
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にクローズされました