AWS CDK Study-4 ~Lambda関数の作成~

API Gatewayと組み合わせて利用するLambda関数を作成します。

# 環境
```
% sw_vers      
ProductName:		macOS
ProductVersion:		14.7.3
BuildVersion:		23H417
% aws --version
aws-cli/2.24.25 Python/3.12.9 Darwin/23.6.0 exe/x86_64
% cdk --version
2.1004.0 (build f0ad96e)
% npm --version
11.1.0
% node --version
v20.17.0
```

# Lambda関数を構築

``` lib/SampleApiResource.ts
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambda_nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import { Construct } from 'constructs';

export interface SampleApiResourceProps {
  product: string;
  environment: string;
  restApi: apigateway.RestApi;
}

export class SampleApiResource extends Construct {
  constructor(scope: Construct, id: string, props: SampleApiResourceProps) {
    super(scope, id);

    const product = props.product;
    const env = props.environment;

    const sampleApiResource = props.restApi.root.addResource('sample');

    const getSampleFunctionId: string = `${product}-${env}-get-sample`;
    const getSampleFunction = new lambda_nodejs.NodejsFunction(
      this,
      getSampleFunctionId,
      {
        entry: '../lambda/api/sample.ts',
        handler: 'handler',
        runtime: lambda.Runtime.NODEJS_22_X,
        memorySize: 512,
        timeout: cdk.Duration.seconds(10),
        bundling: {
          target: 'es2022',
          minify: true,
        },
      },
    );
    sampleApiResource.addMethod(
      'GET',
      new apigateway.LambdaIntegration(getSampleFunction)
    );
  }
}
```
`aws-cdk-lib/aws-apigateway/RestApi`を外部で作成し、Props経由で受け取る想定です。

# 発展
ソフトウェア開発で「Lambda関数が1つだけ」という場面は少ないと思います。
`aws-cdk-lib/aws-lambda-nodejs/NodejsFunction`のPropsには、`entry`、`handler`、`runtime`、etc...と、様々の設定項目があり、それらを、Lambda関数毎に書くのは大変です。
`Node.js`をまとめてバージョンアップしたい時に、何十ヶ所も変えていくのも手間になりそうです。
そこで、ラッパー関数的なものを作成することにしました。

## 作成した関数

``` lib/resources/lambdaNodejsFunction.ts
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambda_nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import type { Construct } from 'constructs';

// Properties default value: interface NodejsFunctionProps
const DEFAULT_HANDLER: string = 'handler';
const DEFAULT_RUNTIME: lambda.Runtime = lambda.Runtime.NODEJS_22_X;
const DEFAULT_MEMORY_SIZE: number = 512; // MB
const DEFAULT_TIMEOUT: cdk.Duration = cdk.Duration.seconds(10);

// Properties default value: interface BundlingOptions
const DEFAULT_TARGET: string = 'es2022';
const DEFAULT_MINIFY: boolean = true;

export interface LambdaNodejsFunctionProps {
  entry: string;
  handler?: string;
  runtime?: lambda.Runtime;
  memorySize?: number;
  timeout?: cdk.Duration;
}

export function buildLambdaNodejsFunction(
  scope: Construct,
  id: string,
  props: LambdaNodejsFunctionProps,
): lambda_nodejs.NodejsFunction {
  const nodejsFunctionProps: lambda_nodejs.NodejsFunctionProps = {
    entry: props.entry,
    handler: props.handler ?? DEFAULT_HANDLER,
    runtime: props.runtime ?? DEFAULT_RUNTIME,
    memorySize: props.memorySize ?? DEFAULT_MEMORY_SIZE,
    timeout: props.timeout ?? DEFAULT_TIMEOUT,
    bundling: {
      target: DEFAULT_TARGET,
      minify: DEFAULT_MINIFY,
    },
  };

  return new lambda_nodejs.NodejsFunction(scope, id, nodejsFunctionProps);
}
```

`new NodejsFunction(...)`の代わりに`buildLambdaNodejsFunction(...)`として利用します。
`Props`で、`entry`のみを必須にして、他を任意にしました。任意の部分は、指定されなければデフォルト値が指定されます。
この関数を利用すると、最初のソースコードは次のようになります。

```lib/SampleApiResource.ts
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import { Construct } from 'constructs';
import { buildLambdaNodejsFunction } from '../lambdaNodejsFunction';

export interface SampleApiResourceProps {
  product: string;
  environment: string;
  restApi: apigateway.RestApi;
}

export class SampleApiResource extends Construct {
  constructor(scope: Construct, id: string, props: SampleApiResourceProps) {
    super(scope, id);

    const product = props.product;
    const env = props.environment;

    const sampleApiResource = props.restApi.root.addResource('sample');

    const getSampleFunctionId: string = `${product}-${env}-get-sample`;
    const getSampleFunction = buildLambdaNodejsFunction(
      this,
      getSampleFunctionId,
      {
        entry: '../lambda/api/sample.ts',
      },
    );
    sampleApiResource.addMethod(
      'GET',
      new apigateway.LambdaIntegration(getSampleFunction),
    );
  }
}
```
ソースコードがスッキリしました。
スペックがデフォルト値で問題ないのであれば、Lamdba関数自体のソースコードへのパスだけを意識してコーディングできるのは相当楽です。

# Next

作成したLambda関数を API Gateway と組み合わせてアクセスできるようにします。