LoginSignup
2
0

More than 3 years have passed since last update.

AWS CDKでLambdaのTypescriptをトランスパイルしてローカル実行したメモ

Last updated at Posted at 2021-03-11

概要

AWS CDKでのビルドに苦労したので備忘録。

ソースコード

やること

  • AWS CDKを使ったトランスパイル
  • SAMを使ったローカル実行
  • 設定によってどんなファイルが出るかの確認

やらないこと

  • デプロイ

環境

  • Windows Home
  • docker desktop 3.2.1
  • wsl2 ubuntu20
  • SAM CLI, version 1.20.0
  • aws-cli/2.1.28 Python/3.8.8 Windows/10 exe/AMD64 prompt/off

前提知識

トランスパイル

  • "esbuild": "^0.9.0"をnpm installすれば、Dockerがなくてもトランスパイル可能
package.json
    "bundle": "cdk synth && cdk synth > cdk.out/template.yaml",
  • cdk synthでデプロイする予定の成果物をローカルに出力できる。
    • template.yamlはローカル実行用

image.png

ローカル実行

  • cdkで出力したtemplage.yamlを使って、samコマンドでローカル実行
    • helloHOGEHOGEはteamplateファイル内から取得すること.Type: AWS::Lambda::Functionの上。(*)
    • template.yamlは改行コードlfの必要がある。gitbashならlfだが、powershellなどだと> template.yamlでcrlfになるので注意
    • gitbashだとsamにパスが通っていないので、sam.cmdを利用している
package.json
    "localinvoke": "cd cdk.out && sam.cmd local invoke hello26396490--no-event",

image.png
image.png

ビルド成果物検討

  • トランスパイルされたjsファイルを見ていく。

Stackの定義は長いので折り畳み
lib/sample-stack.ts

import * as cdk from '@aws-cdk/core';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import * as lambda from '@aws-cdk/aws-lambda';

export class SampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    new NodejsFunction(this, 'test', {
      runtime: lambda.Runtime.NODEJS_14_X,
      entry: 'src/lambda/handlers/test.ts',
      functionName: 'kotatest'
    });
    new NodejsFunction(this, 'test2', {
      runtime: lambda.Runtime.NODEJS_14_X,
      entry: 'src/lambda/handlers/test.ts',
      functionName: 'kotatest',
      bundling: {
        externalModules: [
          'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
          'date-fns', // Layrerに入れておきたいモジュール
        ],
        define: { // Replace strings during build time
          'process.env.API_KEY': JSON.stringify(JSON.stringify('"xxx-xxx"')), // バグってそう
        },
      },
    });
    new NodejsFunction(this, 'hello', {
      runtime: lambda.Runtime.NODEJS_14_X,
      entry: 'src/lambda/handlers/hello.ts',
      functionName: 'kotahello',
      handler: 'lambdaHandler'
    });
  }
}

シンプルなやつ

  • とても素直
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, {get: all[name], enumerable: true});
};

// hello.ts
__markAsModule(exports);
__export(exports, {
  lambdaHandler: () => lambdaHandler
});
var lambdaHandler = async (event, context) => {
  console.log(event);
  console.log(context);
  let response = null;
  try {
    response = {
      statusCode: 200,
      body: JSON.stringify({
        message: "hello world"
      })
    };
  } catch (err) {
    console.log(err);
    return err;
  }
  return response;
};

モジュールを利用しているもの

  • date-fnsを使ったjsは以下の感じ。date-fnsが埋め込まれて長い。
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __commonJS = (callback, module2) => () => {
// 省略
var __toModule = (module2) => {
  return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2);
};

// ../../../node_modules/date-fns/_lib/requiredArgs/index.js
// 省略

// ../utilities/dateUtility.ts
var import_format = __toModule(require_format());
var formatISO = (d) => (0, import_format.default)(d, "yyyy-MM-dd'T'HH:mm:ss");

// test.ts
var handler = async (event) => {
  const test = `Hello from Lambda! TS!${formatISO(new Date())}, key: ${JSON.stringify(process.env.API_KEY)}`;
  const response = {
    statusCode: 200,
    body: JSON.stringify(test)
  };
  return response;
};

externalにモジュールを指定した場合

lib/sample-stack.ts
    new NodejsFunction(this, 'test2', {
      runtime: lambda.Runtime.NODEJS_14_X,
      entry: 'src/lambda/handlers/test.ts',
      functionName: 'kotatest',
+      bundling: {
+        externalModules: [
+          'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
+          'date-fns', // Layrerに入れておきたいモジュール
+        ],
        define: { // Replace strings during build time
          'process.env.API_KEY': JSON.stringify(JSON.stringify('{"value":"xxx-xxx"}')), // バグってそう
        },
      },
    });
  • requireに置き換わってすっきり
// 省略
// ../utilities/dateUtility.ts
var import_date_fns = __toModule(require("date-fns"));
var formatISO = (d) => (0, import_date_fns.format)(d, "yyyy-MM-dd'T'HH:mm:ss");

// test.ts
var handler = async (event) => {
  const test = `Hello from Lambda! TS!${formatISO(new Date())}, key: ${JSON.stringify(define_process_env_API_KEY_default)}`;
  const response = {
    statusCode: 200,
    body: JSON.stringify(test)
  };
  return response;
};
exports.handler = handler;

ビルド時の文字置換を設定したとき

  • 2回JSON.stringifyをする必要があり、バグっていそう
lib/sample-stack.ts
    new NodejsFunction(this, 'test2', {
      runtime: lambda.Runtime.NODEJS_14_X,
      entry: 'src/lambda/handlers/test.ts',
      functionName: 'kotatest',
      bundling: {
        externalModules: [
         'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
          'date-fns', // Layrerに入れておきたいモジュール
        ],
+        define: { // Replace strings during build time
+          'process.env.API_KEY': JSON.stringify(JSON.stringify('"xxx-xxx"')), // バグってそう
+        },
      },
    });
  • JSON.stringfyを2回しているが、1回だけだと > error: Invalid define value (must be valid JSON syntax or a single identifier): {value:xxx-xxx}のエラーがでる

エスケープ別解 2021/04/13 1.97.0で使用不可
  • 以下でもエスケープできそう *
  JSON.stringify(`\\"${'xxx-xxx'}\\"`)

トランスパイル前のソースは以下

  const test: string = `Hello from Lambda! TS!${dateUtility.formatISO(new Date())}, key: ${process.env.API_KEY}`
  const response = {

トランスパイル後のソースは以下


// test.ts
var handler = async (event) => {
  const test = `Hello from Lambda! TS!${formatISO(new Date())}, key: ${"xxx-xxx"}`;

※2021/04/13 1.97.0 でできなくなっている

Done in 5ms
Bundling asset SampleStack2021/test/Code/Stage...
 > error: Invalid define value (must be valid JSON syntax or a single identifier): \"xxx-xxx\"

1 error

D:\develop\develop\tutorial\lesson\aws\typescript\projects\cdk_sample\cdk\node_modules\@aws-cdk\core\lib\asset-staging.ts:472
  for (const key of Object.keys(object).sort()) {
            ^

参考

CDK Workshop
aws cdk
aws cdk / esbuild config
aws cdk / lambda nodejs ts

cdk コマンドの機能を 実際に叩いて理解する 【 AWS CDK Command Line Interface 】
AWS CDK で AWS SAM のテンプレートを出力してデプロイする
AWS CDK Toolkit (cdk command)
SAM Hello World
CDKで作ったLambda@Edgeのコードに環境変数を渡せるようにする
Cloud Formationでしっておきたかったこと

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0