概要
AWS SAM (AWS Serverless Application Model) とは、サーバーを意識せずに構築する Web API ……いわゆる「サーバーレスアプリケーション」を簡単に作成するためのツールキットです。
公式ドキュメントにおけるサンプルでは、 Amazon API Gateway を介して AWS Lambda function をキックし、 AWS Lambda function は Amazon DynamoDB と連携……といった構成も簡単に書けることが示されています。
理屈としては、 AWS SAM CLI を用いて作成したプロジェクトを編集・実行することで、「どういったクラウドリソースが必要か?」を AWS CloudFormation の形式で書き下し、それをデプロイすることで、クラウドリソースが AWS 上に作られる感じ。つまり、既存の手段に加えて新たに、「AWS SAM を使う」というお手軽メソッドも増えたわけですね。
- AWS 上で REST API を構築する手段
- AWS の Web フォーム上でリソースを作る
- AWS CloudFormation (CFn) のテンプレートを手書きする
- AWS Cloud Development Kit (CDK) でプログラムを書く
- AWS SAM を使う ← New!
丁寧な説明は公式のチュートリアルに譲るとして、ここではざっくりとした手順を示します。
- AWS SAM CLI をインストール
- 各 OS 向けにインストーラーが提供されている
sam init
コマンドでプロジェクトを作成- 対話形式でテンプレートやオプションを選択する
- 以下の記述では、「AWS Quick Start Templates」の「Hello World Example TypeScript」を「nodejs20.x」で動作させ、デプロイ手段に「Zip」を選択したものとする
- プロジェクトのディレクトリに移動し、
sam build
でビルド- 事前に
npm i
などとして必要なパッケージを導入しておくこと
- 事前に
- 開発
sam local invoke
で Lambda function の動作を確認- GETパラメーターなどは、
sam local invoke -e event.json
とJSON形式で渡す
- GETパラメーターなどは、
sam local start-api
で REST API の動作を確認sam sync --watch
で差分を自動デプロイ
sam deploy
でデプロイ- デプロイする際は、デプロイ先のリージョンを
--region
オプションで指定するか、環境変数としてAWS_DEFAULT_REGION
を設定しておく
- デプロイする際は、デプロイ先のリージョンを
sam delete
で削除
- フォーマット:
GET /api/aozora-books?title={タイトル}&author={作成者}&maxResults={最大取得件数}
- 説明:国会図書館の検索API を使って、書籍情報を取得する
- 詳しい挙動は PDF で提供されているが、元の API のレスポンスは XML 形式で返ってくるため、必要な部分だけ抽出して JSON 形式で返すようにする
- 同APIは、利用目的や利用先データプロバイダによって、利用申請が必要か変わってくるので注意。今回は、申請不要な「青空文庫」のデータプロバイダをサンプルにする
- プロジェクト名は
aozora-search-app
とした - まずtemplate.yamlを以下のように編集する
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Events:
HelloWorld:
Type: Api
Properties:
Path: /api/aozora-books
Method: get
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/api/aozora-books"
- 次に、
app.ts
を以下のように編集する- エラーチェックは省略している
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
// GETパラメーターを取得
const title = event.queryStringParameters?.title;
const author = event.queryStringParameters?.author;
const maxResults = event.queryStringParameters?.maxResults;
return {
statusCode: 200,
body: JSON.stringify({
title,
author,
maxResults,
}),
};
};
手順2:デプロイ含めた動作確認
- 確認のために
event.json
を作成し、sam local invoke -e event.json
で動作確認する
{
"queryStringParameters": {
"title": "こころ",
"author": "夏目漱石",
"maxResults": "10"
}
}
sam local start-api
でローカル上のWeb APIを立ち上げ、curl
コマンドで動作確認する- 例:
http://127.0.0.1:3000/api/aozora-books/?title=%E3%81%93%E3%81%93%E3%82%8D&author=%E5%A4%8F%E7%9B%AE%E6%BC%B1%E7%9F%B3&maxResults=10
- 例:
> % curl http://127.0.0.1:3000/api/aozora-books/\?title\=%E3%81%93%E3%81%93%E3%82%8D\&author\=%E5%A4%8F%E7%9B%AE%E6%BC%B1%E7%9F%B3\&maxResults\=10 | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 63 100 63 0 0 1967 0 --:--:-- --:--:-- --:--:-- 2032
{
"title": "こころ",
"author": "夏目漱石",
"maxResults": "10"
}
sam deploy --guided
でデプロイする。例えば次のような設定になる
Setting default arguments for 'sam deploy'
=========================================
Stack Name [aozora-search-app]:
AWS Region [us-east-1]: ap-northeast-1
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [Y/n]: y
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: y
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]:
HelloWorldFunction has no authentication. Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]:
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
- デプロイ後は、Webブラウザーなどから動作を確認できる
- 例:
https://example.execute-api.ap-northeast-1.amazonaws.com/Prod/api/aozora-books/?title=%E3%81%93%E3%81%93%E3%82%8D&author=%E5%A4%8F%E7%9B%AE%E6%BC%B1%E7%9F%B3&maxResults=10
- 例:
- エラーチェックなど細かな実装は都度行うこと
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
const { XMLParser } = require('fast-xml-parser');
export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
// GETパラメーターとして、title・author・maxResultsを受け取る
const title = event.queryStringParameters?.title;
const author = event.queryStringParameters?.author;
const maxResults = event.queryStringParameters?.maxResults;
// 入力バリデーション
if (!title && !author) {
return {
statusCode: 400,
body: JSON.stringify({
message: 'title or author is required',
}),
};
}
// APIにアクセスし、結果を取得するためのURLを作成
const url = 'https://ndlsearch.ndl.go.jp/api/sru';
const params = new URLSearchParams();
params.append('operation', 'searchRetrieve');
if (maxResults) {
params.append('maximumRecords', maxResults);
}
let query = `dpid="aozora"`;
if (title) {
query += ` AND title="${title}"`;
}
if (author) {
query += ` AND creator="${author}"`;
}
params.append('query', query);
console.log('URL', `${url}?${params.toString()}`);
// APIにアクセスし、結果を取得
const response = await fetch(`${url}?${params.toString()}`);
// 結果を変換して返す
const xmlData = await response.text();
const parser = new XMLParser();
const javaScriptData = parser.parse(xmlData);
const bookRecords = javaScriptData.searchRetrieveResponse.records.record.map((item: any) => {
const itemData = parser.parse(item.recordData);
return {
'title': itemData['srw_dc:dc']['dc:title'],
'author': itemData['srw_dc:dc']['dc:creator'],
'publisher': itemData['srw_dc:dc']['dc:publisher'],
'description': itemData['srw_dc:dc']['dc:description'],
'langugage': itemData['srw_dc:dc']['dc:language'],
};
});
return {
statusCode: 200,
body: JSON.stringify(bookRecords)
};
};
- 以上により、例えば
https://example.execute-api.ap-northeast-1.amazonaws.com/Prod/api/aozora-books?title=%E3%81%93%E3%81%93%E3%82%8D&author=%E5%A4%8F%E7%9B%AE%E6%BC%B1%E7%9F%B3&maxResults=10
にアクセスすると、結果がJSON形式で返却される
[
{
"title": "『心』広告文",
"author": "夏目 漱石 著者",
"publisher": "岩波書店",
"description": [
"「時事新報」1914(大正3)年9月26日",
"漱石全集 第十六巻",
"岩波書店",
"1995(平成7)年4月19日",
"1995(平成7)年4月19日",
"1995(平成7)年4月19日",
"漱石全集 第十一巻",
"岩波書店",
"1966(昭和41)年10月",
"新字旧仮名"
],
"langugage": "jpn"
},
(以下略)
まとめ
- AWS SAM CLIを用いて、外部APIにアクセスするようなREST APIを作成できた
- キャッシュ機構の搭載、データベースとの連携などは今後の課題とする
- 閲覧数 381
コメントを追加