1
1

More than 3 years have passed since last update.

aws-sdkでAPIGatewayとLambdaの統合をセットアップする

Posted at

概要

APIGatewayからLambdaを呼び出せるようにするまでの設定をaws-sdkを使って自動化したかったのでコードを書いてみました。
特に CORS の有効化の部分はなかなか資料がなく苦労しました。。。

TypeScriptで書いているので型定義不要な方は適時読み替えてください。
上から順につなげれば動きます。

実装コード

REST API の作成

import AWS from "aws-sdk";

AWS.config.update({
  region: "ap-northeast-1",
  accessKeyId: "XXXXXXXXXXXXXXXXXXX",
  secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXX",
});

// APIGateway
const apiGateway = new AWS.APIGateway();

/**
 * REST APIを作成する
 * @param apiName ステージ名
 * @return REST APIのID
 */
const createRestApi = async (apiName: string): Promise<string> => {
  // REST APIを作成する
  const result = await apiGateway.createRestApi({ name: apiName }).promise();
  // REST APIのIDを記録しておく
  const restApiId = result.id;
  return restApiId;
};

リソースの作成

/**
 * REST APIのリソースを作成する
 * @param restApiId REST APIのID
 * @param parentPath 親リソースのパス
 * @param pathPart リソースパス
 * @return Lambda設定
 */
const createRestApiResource = async (restApiId: string, parentPath: string, pathPart: string): Promise<string> => {
  // 親リソースのID
  let parentResourceId: string | undefined = "";
  // リソース一覧を取得する
  const getResourcesResult = await apiGateway.getResources({ restApiId }).promise();
  if (getResourcesResult.items) {
    // 見つかったリソースでループする
    for (const resource of getResourcesResult.items) {
      if (resource.path === parentPath) {
        // 親リソースのIDを取得する
        parentResourceId = resource.id;
        break;
      }
    }
  }
  if (!parentResourceId) {
    // 親リソースが見つからなければエラー
    throw new Error(`親リソース:${parentPath} が見つかりませんでした。`);
  }
  // 親リソースが見つかれば、そこにリソースを追加する
  const result = await apiGateway
    .createResource({
      restApiId: restApiId,
      parentId: parentResourceId,
      pathPart: pathPart,
    })
    .promise();
  // 作成したリソースのIDを取得する
  const resourceId = result.id;
  return resourceId;
};

CORS の有効化 (OPTION メソッドの追加)

/**
 * REST APIのリソースにOPTIONメソッドを作成する
 * @param restApiId REST APIのID
 * @param resourceId リソースのID
 */
export const createRestApiOptionMethod = async (restApiId: string, resourceId: string) => {
  // OPTIONSメソッドを作成する
  await apiGateway
    .putMethod({
      restApiId: restApiId,
      resourceId: resourceId,
      httpMethod: "OPTIONS",
      authorizationType: "NONE",
    })
    .promise();
  // メソッドレスポンスを作成する
  await apiGateway
    .putMethodResponse({
      restApiId: restApiId,
      resourceId: resourceId,
      httpMethod: "OPTIONS",
      statusCode: "200",
      responseParameters: {
        "method.response.header.Access-Control-Allow-Headers": false,
        "method.response.header.Access-Control-Allow-Methods": false,
        "method.response.header.Access-Control-Allow-Origin": false,
      },
    })
    .promise();
  // 統合リクエストを作成する
  await apiGateway
    .putIntegration({
      restApiId: restApiId,
      resourceId: resourceId,
      httpMethod: "OPTIONS",
      integrationHttpMethod: "OPTIONS",
      type: "MOCK",
      contentHandling: "CONVERT_TO_TEXT",
      requestTemplates: {
        "application/json": '{"statusCode": 200}',
      },
    })
    .promise();
  // 統合レスポンスを作成する
  await apiGateway
    .putIntegrationResponse({
      restApiId: restApiId,
      resourceId: resourceId,
      httpMethod: "OPTIONS",
      statusCode: "200",
      responseParameters: {
        "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'",
        "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'",
        "method.response.header.Access-Control-Allow-Origin": "'*'",
      },
    })
    .promise();
};

メソッドを追加して Lambda を呼び出すようにする

/**
 * REST APIのリソースにPOSTメソッドを作成する
 * @param restApiId REST APIのID
 * @param resourceId リソースのID
 * @param httpMethod メソッド名
 * @param lambdaArn 呼び出すLambdaのARN
 */
export const createRestApiPostMethod = async (restApiId: string, resourceId: string, httpMethod: string, lambdaArn: string) => {
  // メソッドを作成する
  await apiGateway.putMethod({ restApiId, resourceId, httpMethod: httpMethod, authorizationType: "NONE" }).promise();
  // 統合リクエストを作成する
  await apiGateway
    .putIntegration({
      restApiId: restApiId,
      resourceId: resourceId,
      httpMethod: httpMethod,
      integrationHttpMethod: httpMethod,
      type: "AWS_PROXY", // Lambdaプロキシ統合の使用
      contentHandling: "CONVERT_TO_TEXT",
      uri: `arn:aws:apigateway:${AWS.config.region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations`,
    })
    .promise();
};

デプロイする

/**
 * REST APIをデプロイする
 * @param restApiId APIのID
 * @param stageName ステージ名
 */
const deployRestApi = async (restApiId: string, stageName: string) => {
  await apiGateway.createDeployment({ restApiId, stageName: stageName }).promise();
};

使用例

(async () => {
  // LambdaのARNは事前に分かっているものとする
  const lambdaArn = "arn:aws:lambda:ap-northeast-1:XXXXXXXXX:function:YYYYYYYYYYY";
  // REST APIを作成する
  const restApiId = await createRestApi("test-api");
  // hogeリソースを作成する
  const resourceId = await createRestApiResource(restApiId, "/", "hoge");
  // hogeリソースのCORSを有効化する (OPTIONSメソッドを作成)
  await createRestApiOptionMethod(restApiId, resourceId);
  // hogeリソースにPOSTメソッドを作成し、Lambdaを呼び出すようにする
  await createRestApiPostMethod(restApiId, resourceId, "POST", lambdaArn);
  // ステージ:dev としてデプロイする
  await deployRestApi(restApiId, "dev");
})();
1
1
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
1
1