DEVELOPMENT
目次
S3、Cloudfrontにてwebサイトを公開してますが、jsからSNS(ここではInstagram)のAPIを使用する際、気になるのがAccess token。
Access token をパラメータに含みたくないので、AWSのAPI gatewayでラッピングして、使用するようにしました。
更にそのAPIをAWS_IAM認証制限し、Cognitoを使って node 環境から叩く方法についてメモしておこうと思います。
各種必要なAPIの準備
僕は今回InstagramのAPIを叩きたかったので、下記を参考にdeveloper登録をして、access tokenを取得してAPIを叩けるようにしました。
API Gatewayの設定
処理の流れとしては下記です。
- API Gateway を叩く
- API GatewayからLambda関数が呼ばれる
- Labda関数が、必要なパラメータ等の処理をして、Instagram APIを叩く
- レスポンス返却
それではAPI Gatewayを設定していきます。
AWSコンソールにログイン -> API Gatewayへ -> APIの作成をクリック
リソースの作成
API自体が作成されたので、次にリソースを作成します。 アクション -> リソースを作成 を選択します。
- リソース名
- API Gateway CORSを有効にするにチェック
メソッドの作成
作成されたリソースを選択した状態で、アクション -> メソッドの作成を選択します。
セレクトボックからGETを選択し、チェックマークをクリック
ただAPIを叩くラッピングするだけなら、ここで統合タイプをHTTPを選択し、エンドポイントにそのAPIを指定すればOKですが、ただ、client から渡ってきた Query String を endpoint に渡す際、key は変更できるものの、value を変えたり、特定の key を静的に(=固定で)指定といったことはできないようですので、今回はLambda関数を使用します。
今回はAccess tokenを静的に渡したいので、lambda関数を使用します
API Gateway + Lambda で固定のパラメータを付加して HTTP Proxy する
クエリパラメータの設定
今回はinstagram api で投稿データを数軒取得しましたが、全件取得しても無駄なので、必要な件数のみ取得しようと思います。Instagra api 側で countというクエリパラメータがあるので、これをAPI gatewayで設定したAPIにも付加できるように設定します。 GETメソッドを選択状態にし、「メソッドリクエストから」クエリ文字列の追加から、クエリ名を入力します。
次に「統合リクエスト」から一番下***本文マッピングテンプレート***から、追加し、content-typeを「application/json」に設定し下記のように設定します。
※、ダブルクォートで囲わないとうまく行きません。
ここまでで、一旦API Gatewayの設定は終わりです。後に、deployとアクセスの制限の設定をします。
次にLambda関数を準備します。
Lambda関数の設定
関数の作成
AWSコンソールより、Lambdaを選択し、関数の作成をクリック
トリガーの設定
トリガーを追加します。
関数のコード作成
今回はnode.jsを使います。 local環境にて、
$ yarn init
もしくは
$ npm init
し、projectを作成します。
必要なパッケージを入れます。
$ yarn add requrest
もしくは
$ npm install request --save
jsファイルを作成します。
- index.js
var request = require('request');
exports.handler = function(event, context) {
var params = { count:event.count, access_token: process.env.ACCESS_TOKEN};
request.get({url:process.env.URL, qs: params}, function(err, res, body) {
if (!err && res.statusCode == 200) {
context.done(null, body);
} else {
console.log('error: '+ res.statusCode);
}
});
};
- process.envは、後ほどLambda 側で設定します。
- 単純に、requestを使って、APIを叩く処理です。
- event.countは先程API Gatewayで設定した、クエリパラメータになります。
次にこれら(index.jsと作成されたnode_modulesの2つ)をLambdaにアップロードするために、zipで圧縮します。
アップロードします。
環境変数を設定
コード中のprocess.envの値を設定します。
これでLambdaの設定は以上になります。
API Gatewayをdeployする
デプロイ
AWSコンソールにログイン -> 再びAPI Gatewayへ -> APIの作成をクリック 先程作成した、APIを選択します。
API ルートを選択し、アクションから、APIのデプロイを選択
エンドポイントの確認
左メニューのステージを選択する
ここまでで、APIのラッピング作業は終わりです。実際に表示されているURLを叩いて結果が帰ってくればOKです。 設定した、クエリパラメータ(count)も指定してみましょう。
これでOKですが、APIのURLがバレたら誰でも叩けてしまうのはちょっと嫌なので(jsから使うため)、Cognitoを使って制限します。
API Gatewayに認証をつける
AWSコンソールにログイン -> 再びAPI Gatewayへ -> APIの作成をクリック 先程作成した、APIを選択します。 GET を選択し、認証をAWS_IAMに設定します。
これで先程と同じようにデプロイし、エンドポイントに表示されているURLをたていてもデータを取得できないことを確認します。
ARNのメモ
API Gatewayの画面で、ARNをメモしておきます。
Cognitoの設定
プールIDの作成
AWSコンソールにログイン -> Cognitoへ -> フェデレーテッドアイデンティティの管理を選択 -> 新しい ID プールの作成へ プールIDを作成します
ポリシーの設定
ポリシードキュメントを下記のように設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:xx-xxxx-xx:xxxxxxxxxxxx:xxxxxxxxxx/*/GET/instagram"
]
}
]
}
- Resource部分は、execute-api:リージョン:arn-idになります。先程メモった内容です。
jsコードを取得
これでcognitoの設定はOKです。
Node 環境から実際に使ってみる
AWS API Gateway は便利なサービスですが、Node.js から使おうとすると、自動で生成される JavaScript のSDKがブラウザ用しか用意されていなかったり、エンドポイントごとにメソッドが自動で生成されていて、APIのエンドポイントを変更するごとにSDKを生成し直さなければならなかったりと不便だったため、Node.js から API Gateway を簡単に使えるパッケージを作ってくれた方がいらっしゃいましたので、こちらを使用させていただきました。ありがとうございます。 Node.js から API Gateway を簡単に使えるパッケージ作りました
準備
$ yarn add aws-sdk
$ yarn add aws-api-gateway-client
もしくは
$ npm install add aws-sdk
$ npm install aws-api-gateway-client
※parcelを使っていますが、babelモジュール等の依存があるので、このパッケージで使っているモジュールを入れます
.babelrc
{
"presets": [
"stage-0",
"@ava/stage-4",
"babel-preset-es2015",
"babel-preset-es2016",
"babel-preset-es2017",
],
"plugins": ["transform-es5-property-mutators"]
}
上記が必要なものですので、すべてnpm or yarnで入れます。各moduleについてはググれば出てきますのでお願いします。 2018/03 これは、babelrcに入れなくても、aws-api-gateway-clientに上記が必要なだけですので、install しておくだけでOKです。
$ yarn add --dev babel-plugin-transform-es5-property-mutators @ava/babel-preset-stage-4 babel-preset-stage-0 babel-preset-es2015 babel-preset-es2016 babel-preset-es2017
OR
$ npm install --save-dev babel-plugin-transform-es5-property-mutators @ava/babel-preset-stage-4 babel-preset-stage-0 babel-preset-es2015 babel-preset-es2016 babel-preset-es2017
コード
import AWS from 'aws-sdk'
let apigClientFactory = require('aws-api-gateway-client').default
// Amazon Cognito
AWS.config.region = 'xxxxxxxxxxx'
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
})
AWS.config.credentials.get(function(err){
if(!err) {
const apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId,
secretKey: AWS.config.credentials.secretAccessKey,
sessionToken: AWS.config.credentials.sessionToken,
region: 'xxxxxxxx',
invokeUrl: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
});
let params = {
//This is where any header, path, or querystring request params go. The key is the parameter named as defined in the API
};
// Template syntax follows url-template https://www.npmjs.com/package/url-template
let pathTemplate = 設定したAPIのパス
let method = 'GET'
let additionalParams = {
//If there are any unmodeled query parameters or headers that need to be sent with the request you can add them here
queryParams: {
count: 必要な件数,
}
};
let body = {
//This is where you define the body of the request
};
let items = []
apigClient.invokeApi(params, pathTemplate, method, additionalParams, body)
.then(function(result){
// 成功処理
}).catch( function(result){
//This is where you would put an error callback
// エラー処理
});
}else{
console.log(err)
}
}
})
以上です。
まとめ
これで安全にAPIを叩くことができるようになりました。 意外とnode環境からAPI gatewayを叩くすべがあまりなく、たまたま作ってくださっていた方がいらっしゃいましたので、助かりました。 ありがとうございました。
株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから