LoginSignup
9
8

More than 5 years have passed since last update.

Kintone REST APIを呼ぶAmazon APIGatewayをCloudFormationで作る

Last updated at Posted at 2016-04-27

これまで、以下の記事でAPIGatewayからKintone REST APIを呼んでみましたが、CloudFormationがAPIGatewayに対応したので、ここまでやったことをCloudFormationのtemplate化してみました。(swaggerは使ったこと無い :sweat_smile:

注意
kintone REST APIは同時アクセス数がドメインごとに10と制限されています。

ドメインへの同時アクセス
 ・APIによる同時アクセス数はドメインごとに10が上限です。

REST APIの共通仕様

実システムで利用する場合は、API Gatewayのスロットリング設定やエラーのリトライなどを考慮する必要が有ります。

注意ここまで

上記の記事でやったことの他にCORS対応もやってしまいます。

CORSについては、greenspaさんがわかりやすくまとめてくれています。

ソース

{
  "AWSTemplateFormatVersion":"2010-09-09",
  "Description":"Sample stack for Kintone Proxy",
  "Parameters":{
    "StageName":{
      "Type":"String",
      "MinLength":1,
      "Default":"prod"
    },
    "KintoneSubdomain":{
      "Type":"String",
      "MinLength":1
    },
    "KintoneApiToken":{
      "Type":"String",
      "MinLength":1
    },
    "KintoneAppId":{
      "Type":"String",
      "MinLength":1
    },
    "AccessControlAllowOrigin":{
      "Type":"String",
      "MinLength":1,
      "Default":"*"
    }
  },
  "Metadata":{
    "AWS::CloudFormation::Interface":{
      "ParameterGroups":[
        {
          "Label":{"default":"API Gateway configuration."},
          "Parameters":["StageName"]
        },
        {
          "Label":{"default":"Kintone configuration."},
          "Parameters":["KintoneSubdomain","KintoneApiToken","KintoneAppId"]
        }
      ]
    }
  },
  "Resources":{
    "RestApi":{
      "Type" : "AWS::ApiGateway::RestApi",
      "Properties" : {
        "Description" : {"Fn::Join":["",["Create from ",{"Ref":"AWS::StackName"}]]},
        "FailOnWarnings" : true,
        "Name" : {"Fn::Join":["",[{"Ref":"AWS::StackName"},"-Api"]]}
      }
    },
    "ApiMethodPOST":{
      "Type" : "AWS::ApiGateway::Method",
      "Properties" : {
        "ResourceId" : {"Fn::GetAtt":["RestApi","RootResourceId"]},
        "RestApiId":{"Ref":"RestApi"},
        "HttpMethod" : "POST",
        "AuthorizationType" : "NONE",
        "ApiKeyRequired" : false,
        "Integration" : {
          "Type" : "HTTP",
          "Uri" : "https://${stageVariables.subdomain}.cybozu.com/k/v1/record.json",
          "IntegrationHttpMethod" : "POST",
          "RequestParameters" : {
            "integration.request.header.X-Cybozu-API-Token":"stageVariables.apiToken"
          },
          "RequestTemplates" : {
            "application/json":{"Fn::Join":["",[
                "{","\n",
                "    \"app\":\"${stageVariables.appId}\",","\n",
                "    \"record\":{","\n",
                "        \"item1\":{\"value\":$input.json('$.item1')},","\n",
                "        \"item2\":{\"value\":$input.json('$.item2')}","\n",
                "    }","\n",
                "}"
              ]]}
          },
          "IntegrationResponses" : [
            {
              "StatusCode" : "200",
              "ResponseParameters" : {
                "method.response.header.Access-Control-Allow-Origin":
                  {"Fn::Join":["",[
                      "'",
                      {"Ref":"AccessControlAllowOrigin"},
                      "'"
                    ]]}
              },
              "ResponseTemplates" : {
                "application/json": ""
              }
            },
            {
              "StatusCode" : "400",
              "SelectionPattern" : "[^2]\\d{2}",
              "ResponseParameters" : {
                "method.response.header.Access-Control-Allow-Origin":
                  {"Fn::Join":["",[
                      "'",
                      {"Ref":"AccessControlAllowOrigin"},
                      "'"
                    ]]}
              },
              "ResponseTemplates" : {
                "application/json": ""
              }
            }
          ]
        },
        "MethodResponses" : [
          {
            "StatusCode" : "200",
            "ResponseParameters" : {
              "method.response.header.Access-Control-Allow-Origin": false
            }
          },
          {
            "StatusCode" : "400",
            "ResponseParameters" : {
              "method.response.header.Access-Control-Allow-Origin": false
            }
          }
        ]
      }
    },
    "ApiMethodOPTIONS":{
      "Type" : "AWS::ApiGateway::Method",
      "Properties" : {
        "ResourceId" : {"Fn::GetAtt":["RestApi","RootResourceId"]},
        "RestApiId":{"Ref":"RestApi"},
        "HttpMethod" : "OPTIONS",
        "AuthorizationType" : "NONE",
        "ApiKeyRequired" : false,
        "Integration" : {
          "Type" : "MOCK",
          "RequestTemplates" : {
            "application/json":"{\"statusCode\":200}"
          },
          "IntegrationResponses" : [
            {
              "StatusCode" : "200",
              "ResponseParameters" : {
                "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
                "method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
                "method.response.header.Access-Control-Allow-Origin": {"Fn::Join":["",[
                      "'",
                      {"Ref":"AccessControlAllowOrigin"},
                      "'"
                  ]]}
              },
              "ResponseTemplates" : {
                "application/json":""
              }
            }
          ]
        },
        "MethodResponses" : [
          {
            "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
            },
            "ResponseModels" : {
              "application/json": "Empty"
            }
          }
        ]
      }
    },
    "ApiDeployment":{
      "DependsOn":[
        "ApiMethodPOST",
        "ApiMethodOPTIONS"
      ],
      "Type" : "AWS::ApiGateway::Deployment",
      "Properties" : {
        "RestApiId" : {"Ref":"RestApi"},
        "StageName" : {"Ref":"StageName"},
        "StageDescription":{
          "Variables" : {
            "subdomain":{"Ref":"KintoneSubdomain"},
            "apiToken":{"Ref":"KintoneApiToken"},
            "appId":{"Ref":"KintoneAppId"}
          }
        }
      }
    }
  },
  "Outputs":{
    "RestApiEndpoint":{
      "Value":{"Fn::Join":["",[
          "https://",
          {"Ref":"RestApi"},
          ".execute-api.",
          {"Ref":"AWS::Region"},
          ".amazonaws.com/",
          {"Ref":"StageName"},
          "/"
        ]]}
    }
  }
}

解説

  • CloudFormation StackのParameterでKintone関連の情報を渡すと、StageVariablesに設定します。
  • Metadataの部分は、ManagementConsoleでのParameter入力時の見た目を制御しています。
  • 今回の例ではAPI Keyも認可も行っていないのでプリフライトリクエストでのAccess-Control-Allow-Headersに使われないヘッダが含まれています。
  • APIGatewayのHTTP ProxyでKintoneのREST APIを呼ぶ(1)でKitone側で日本語のフィールドコードがあると何か良くないことが起こるようなことを書きましたが、CloudFormationのtemplateで日本語をかけないので、RequestTemplatesで詰みました。

ブラウザから実行

テスト用HTML

<html>
<head>

</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script>
  var settings = {
    "async": true,
    "crossDomain": true,
    "url": "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/",
    "method": "POST",
    "headers": {
      "content-type": "application/json"
    },
    "processData": false,
    "data": "{\n    \"item1\":\"from Browser\",\n    \"item2\":\"item2\"\n}"
  }

  $.ajax(settings).done(function (response) {
    console.log(response);
  });
</script>
</body>
</html>

実行結果(ChromeのDeveloperToolより)

OPTIONS (プリフライトリクエスト)

image

POST

image

結果

image

(Kintone側は省略)

、、といとも簡単にできてしまいました。

僕は、パラメータも渡せてDeploy・ApiKeyに対応しているCloudFormationがSwaggerよりも好きです!(宣言)

では!

9
8
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
9
8