Amazon API Gatewayをブラウザから呼んでみた

API Gateway

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

好物はインフラとフロントエンドのかじわらゆたかです。

Amazon API Gatewayのノウハウがいろいろ出ているので、
ブラウザから動かして見ようと思います。

流れとしてはAWS Lambdaで、S3 Pre-Signed URLの生成を実装し、
それをAmazon API Gatewayにデプロイします。

その後、ブラウザからデプロイされたAPIを実行し、
生成されたS3 Pre-Signed URLからアップロードされた画像を取得するといった形になります。

Pre-Signed URLの生成

AWS SDKを用いて、Presigned-URLを発行します。
AWS Gateway自体はTokyoリージョンには来ていませんが、
取得するS3のオブジェクトはTokyoリージョンから取得するといったケースで実装してみます。

app.js

'use strict';
var AWS = require('aws-sdk');
var bucket = 'cm-kajiwara-image';
var region = 'ap-northeast-1';
var s3 = new AWS.S3({
    'region': region
});
var key = 'classmethod-icatch.png';
var params = {
    Bucket: bucket,
    Key: key,
    Expires: 60 
};
exports.handler = function(event, context) {
    s3.getSignedUrl('getObject', params, function(err, url) {
        if(!err){
            context.done(null,url);
        }
    });
};

以前紹介したAWS Lambda Functionをローカルで動かすdriver.jsを用い、 問題なく動くことを確認します。

$ node ./driver.js
https://cm-kajiwara-image.s3-ap-northeast-1.amazonaws.com/classmethod-icatch.png?AWSAccessKeyId=(Your AWS Access Key)&Expires=1436681110&Signature=(generated Signature)

今回Lambdaパッケージを作成するに辺り、
以下のページのサンプルプロジェクトを使いました。
AWS Lambdaの関数をnpmでパッケージ管理 - Qiita

このプロジェクトを用いることで、npmからAWS Lambda用のパッケージを作成・デプロイまで行うことが可能です。
上記のパッケージを使う際にハマった点を共有しておきます。

Scriptで定義されているコマンド郡の動かし方がわからない。

npm testだけは動いたのですが、他のbuild等のコマンドがうまく動きませんでした。
以下のようにすることでScript内で定義されているコマンドを動かすことが可能でした。

$npm run build

Roleの指定方法がわからない。

AWS Lambda Functionをアップロードする際に、
設定ファイルとして作成するlambdaConfig.jsonの内のRoleの記述方法わからず悩みました。
こちらはAWS Management Console内のIAM > Roles 内のarn:aws:iam::から始まる文字列を入力する必要があります。

npm run publishがうまくできない

package.jsonで指定しているaws-sdkのバージョンを更新した際に発生しました。
Lambdaのオブジェクトを作成する際に、apiVersionを明記すること必要がありました。

./script/publish.js

var lambda  = new aws.Lambda({apiVersion:'2014-11-11',  region: lambdaConfig.region}),
    zipPath = 'pkg/' + pkgConfig.name + '.zip';

publishしたfunctionのテストに失敗する。

npm run initLambdaで作成したひな形ファイルにはhandlerFile・handlerMethod が作成されていないのが原因です。
上記の作成と、未設定時にapp.jsに定義したhandlerでアップロードするようにしてみました。

./script/initLambda.js

var fs = require('fs');
var config = {
  region: '',
  description: '',
  role: '',
  memorySize: '128',
  timeout: '3',
  handlerFile:'',
  handlerMethod:''
};
fs.writeFileSync('./lambdaConfig.json',JSON.stringify(config));

./script/publish.js

function buildHnadlerName(lambdaConfig){
    if(!lambdaConfig.handlerFile){
        lambdaConfig.handlerFile = 'app';
    }
    if(!lambdaConfig.handlerMethod){
        lambdaConfig.handlerMethod = 'handler'
    }
  return lambdaConfig.handlerFile + '.' + lambdaConfig.handlerMethod;
}

なお、上記の修正を適用した版を以下のブランチに配置しました。
こちらも併せて見ていただければ幸いです。
CM-Kajiwara/myFirstLambda

Amazon API Gatewayに作ったAWS Lambda Functionをデプロイする。

Deployに関しては、下記のブログを参照していただければと思います。
また、今回の作例ではAPI認証も実施しています。
Amazon API Gateway – API作成から動作確認までやってみる | Developers.IO
Amazon API GatewayでAPIキー認証を設定する | Developers.IO  

ブラウザからAmazon API Gatewayを呼ぶ

今回の作例では、Amazon API GatewayとWebサーバーとクロスドメインになるため、   ブラウザからCORSの設定を行う必要があります。 Amazon API Gateway をクロスオリジンで呼び出す (CORS) | Developers.IO

CORSの設定まで行えれば、呼び出すのはjQueryからでも、
生成されたSDKからでも呼び出すことが可能です。

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>API Gateway Smple</title>
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script type="text/javascript" src="lib/axios/dist/axios.standalone.js"></script>
    <script type="text/javascript" src="lib/CryptoJS/rollups/hmac-sha256.js"></script>
    <script type="text/javascript" src="lib/CryptoJS/rollups/sha256.js"></script>
    <script type="text/javascript" src="lib/CryptoJS/components/hmac.js"></script>
    <script type="text/javascript" src="lib/CryptoJS/components/enc-base64.js"></script>
    <script type="text/javascript" src="lib/url-template/url-template.js"></script>
    <script type="text/javascript" src="lib/apiGatewayCore/sigV4Client.js"></script>
    <script type="text/javascript" src="lib/apiGatewayCore/apiGatewayClient.js"></script>
    <script type="text/javascript" src="lib/apiGatewayCore/simpleHttpClient.js"></script>
    <script type="text/javascript" src="lib/apiGatewayCore/utils.js"></script>
    <script type="text/javascript" src="apigClient.js"></script>
    <script>
        $(document).ready(function() {
            'use strict';
            var gateWayURL = 'Input Your Amazon Gateway URL'
            var APIKey = 'Input Your Amazon Gateway API Key'
            $('#callSDKFunction').click(function(e) {
                var apigClient = apigClientFactory.newClient({
                    apiKey: APIKey
                });
                apigClient.rootGet()
                    .then(function(result) {
                        $('#imageContainer').append($('<img src="' + result.data + '"' + ' width="200" height="200" >'));
                    }).catch(function(result) {
                        //This is where you would put an error callback
                    });
            });
            $('#calljQueryAjax').click(function(e) {
                $.ajax({
                    url: gateWayURL,
                    type: 'GET',
                    headers: {
                        'x-api-key': APIKey
                    }
                }).done(function(result) {
                    $('#imageContainer').append($('<img src="' + result + '"' + ' width="200" height="200" >'));
                });
            });


        });
    </script>
</head>

<body>
    <button id="callSDKFunction">Sample Click callSDKFunction</button>
    <button id="calljQueryAjax">Sample Click calljQueryAjax</button>
    <div id="imageContainer"></div>
</body>

</html>

上記を動かした画像が下記になります。
ボタンを押下することで、Amazon API GatewayよりLambdaから
S3のPre-Signed URLを取得し、imgタグに挿入しているといった流れになります。
20150712

まとめ

API Gatewayをブラウザから扱ってみました。
CORSの設定が必要ではありますが、
そこさえ押さえてしまえば普通のREST APIとして呼び出すことができました。

  • Akinori IKEDA

    郡じゃなくて群っす。内容はめっちゃ参考になりました〜

    • 梶原裕

      Akinori IKEDAさん
      ご指摘ありがとうございます。
      修正いたしました。