🎺

【Windows】AmplifyのLambda function(+Layer)をTypeScriptで書きたい

2022/04/17に公開

実現したいこと

AmplifyのLambdaランタイムで「NodeJS」を選択したときに生成されるテンプレートファイルはデフォルトでTypeScriptに対応していません(2022/04時点)

amplify push時にTypeScriptをコンパイルする方法が公式で紹介されていますが、Lambda Layersを使用している場合の書き方やWindows環境に対応した書き方にするために設定をいじったので、備忘として手順を残しておきます。

環境

本記事では、以下の環境で実施しています。

  • バージョン
    • OS: Windows 11
    • Node.js: v14.16.1
    • npm: 6.14.12
    • AWS CLI: 2.1.38
    • Amplify CLI: 8.0.0
    • Lambdaランタイム: nodejs14.X
  • ネーミング
    • Amplify Project Name: app
    • Lambda Layer Name: layer
      ※識別用としてAmplify Project Name + Lambda Layer Nameのapplayerになります
    • Lambda Function Name: func
  • エディタ
    • Visual Studio Code(以下、VSCode)
  • ターミナル
    • PowerShell

手順

Step1. Lambda Layerを作成する

1. amplify add functionで追加

Powershell
$ amplify add function
? Select which capability you want to add: Lambda layer (shared code & resource used across functions)
? Provide a name for your Lambda layer: layer
? Choose the runtime that you want to use: NodeJS
? The current AWS account will always have access to this layer.
Optionally, configure who else can access this layer. (Hit <Enter> to skip) Specific AWS accounts
? Provide a list of comma-separated AWS account IDs: <AWSアカウントID>
✅ Lambda layer folders & files created:
amplify\backend\function\applayer

2. 共通利用するパッケージをインストール

次のフォルダーに移動

$ cd amplify\backend\function\applayer\lib\nodejs

今回は「date-fns」をインストールしてみます

$ npm install date-fns

ここまでのフォルダ構成

<プロジェクトルート>\amplify\backend\function\applayer\lib\nodejs
│  package-lock.json
│  package.json
│  README.txt
│  
└─node_modules
    └─date-fns

3. 共通利用する処理をTypeScriptで記述

次のフォルダーに移動

$ cd amplify\backend\function\applayer\opt

TSファイルを作成(ここではPowershellのコマンドで新規作成しています)

$ New-Item layer.ts

適当に処理を書きます

layer.ts
// @ts-ignore
import { format as _format } from "date-fns";
// @ts-ignore
import ja from "date-fns/locale/ja";

export const formatJa = (date: Date, format: string): string => {
  return _format(date, format, { locale: ja });
};

2022/04/22追記:以下のようにpackage.jsonのmainの修正が必要
「index.js」→「layer.js」

4. tsconfig.jsonを作成する

TSエラーが出るので、1つ上のフォルダーに移動してtsconfig.jsonを作成する

$ cd ../
$ tsc --init

中身は適宜書き換え

tsconfig.json
{
  "compilerOptions": {
    "target": "es2017",
    "noImplicitAny": false,
    "allowJs": true,
    "noImplicitUseStrict": true,
    "types": ["node"],
    "moduleResolution": "node",
    "module": "CommonJS",
    "baseUrl": "./",
    "paths": {
      "*": ["./lib/nodejs/node_modules/@types/*", "./lib/nodejs/node_modules/*"]
    }
  },
  "include": ["./opt/*.ts"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

VSCodeを使用していると、TSエラーが解消されないことがあるので、
TSサーバーを再起動するか 一度エディターを開き直す

5. 中間フォルダ構成

ここまでのフォルダ構成

<プロジェクルート>\amplify\backend\function\applayer  # 手順1で作成
│  applayer-awscloudformation-template.json
│  layer-configuration.json
│  parameters.json
│  tsconfig.json  # 手順4で作成
│  
├─lib
│  └─nodejs
│      │  package-lock.json
│      │  package.json
│      │  README.txt
│      │  
│      └─node_modules
│          └─date-fns  # 手順2で作成
│                          
└─opt
        layer.ts  # 手順3で作成

Step2. Lambda Functionを作成する

1. amplify add functionで追加

? Provide existing layers or select layers in this project to access from this functionのところで、Step1で作成したLambda Layer(ここではapplayer)を選択する

Powershell
$ amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: func
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World

Available advanced settings:
- Resource access permissions
- Scheduled recurring invocation
- Lambda layers configuration
- Environment variables configuration
- Secret values configuration

? Do you want to configure advanced settings? Yes
? Do you want to access other resources in this project from your Lambda function? No
? Do you want to invoke this function on a recurring schedule? No
? Do you want to enable Lambda layers for this function? Yes
? Provide existing layers or select layers in this project to access from this function (pick up to 5): applayer
? Do you want to configure environment variables for this function? No
? Do you want to configure secret values this function can access? No
? Do you want to edit the local lambda function now? Yes
Edit the file in your editor: <プロジェクトルート>amplify\backend\function\func\src\index.js
? Press enter to continue 
Successfully added resource func locally.

2. tsconfig.jsonを作成する

次のフォルダーに移動

$ cd amplify\backend\function\func\src

後でTSファイルを作成するので、先にtsconfig.jsonを作成しておく

$ tsc --init

中身は適宜書き換え

tsconfig.json
{
  "compilerOptions": {
    "target": "es2017",
    "noImplicitAny": false,
    "allowJs": true,
    "noImplicitUseStrict": true,
    "types": ["node"],
    "module": "CommonJS",
    "baseUrl": "./",
    "paths": {
      "/opt/layer": ["../../applayer/opt/layer"],
      "*": [
        "../../applayer/lib/nodejs/node_modules/@types/*",
        "../../applayer/lib/nodejs/node_modules/*"
      ]
    }
  },
  "include": ["."],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

3. index.tsを作成

次に、TSファイルを作成(ここではPowershellのコマンドで新規作成しています)

$ New-Item index.ts

index.tsの処理を書きます

index.ts
import { formatJa } from "/opt/layer";
import { Callback, Context, Handler } from "aws-lambda";

interface IEvent {
  arguments: {
    input: {
      message: string;
    };
  };
}

interface IResult {
  statusCode: number;
  body: string;
}

/**
 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
 */
export const handler: Handler<IEvent, IResult> = async (
  event: IEvent,
  context: Context,
  callback: Callback<IResult>
) => {
  const { message } = event.arguments.input;
  return {
    statusCode: 200,
    body: `${message} ${formatJa(new Date(), "yyyy-MM-dd")}`,
  };
};

ここまでのフォルダ構成

<プロジェクトルート>\amplify\backend\function\func # 手順1で作成
│  custom-policies.json
│  func-cloudformation-template.json
│  parameters.json
│  
└─src
    │  event.json
    │  index.js
    │  index.ts  # 手順3で作成
    │  package-lock.json
    │  package.json
    │  tsconfig.json  # 手順2で作成

Step3. ローカルの状態をAWS上に反映(push)する

1. プロジェクトルートのpackage.jsonにコードを追加

package.json
...
  "scripts": {
    ...
    "amplify:applayer": "cd amplify/backend/function/applayer/opt && tsc",
    "amplify:func": "cd amplify/backend/function/func/src && tsc"
  },
...

2. amplify function pushで反映

プロジェクトルートに戻って、amplify function pushコマンドを実行

Powershell
$ amplify function push

    Current Environment: <環境名>
    
┌──────────┬───────────────────┬───────────┬───────────────────┐
│ Category │ Resource name     │ Operation │ Provider plugin   │
├──────────┼───────────────────┼───────────┼───────────────────┤
│ Function │ applayer          │ Create    │ awscloudformation │
├──────────┼───────────────────┼───────────┼───────────────────┤
│ Function │ func              │ Create    │ awscloudformation │
└──────────┴───────────────────┴───────────┴───────────────────┘
? Are you sure you want to continue? Yes

Suggested configuration for new layer versions:

applayer

  - Access permissions: Maintain existing permissions
  - Description: Updated layer version  <yyyy-MM-ddTHH:mm:ss.SSSZ>

? Accept the suggested layer version configurations? Yes

後はテストして問題なければ、完了

テストを作成


テスト結果

2022/04/22追記

ローカル環境でTSファイルをJSファイルにコンパイルする方法について書きました。
https://zenn.dev/shibad/articles/ff15dab04c034c

Discussion