こんにちは!サーバーワークスの松井です!
サーバーレスでAPIを作成し 、WEBページからボタンを押してAPIを実行させてみようという記事になります。
このハンズオンは、VScodeでEC2にRemortSSHをして実施していただくことを想定しております。
AWSでEC2を起動してVScodeでログインしてから試してください。
スクリプトについては、基本的にコピペで作成可能ですが、不要なインデントがないかやコメントを外すなど注意が必要です。
構成図

backendを構築
Serverless Fremeworkを使うためにnodenv環境を構築
$ sudo yum -y install git-all $ mkdir handson $ cd handson $ git clone https://github.com/riywo/anyenv ~/.anyenv $ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bashrc $ echo 'eval "$(anyenv init -)"' >> ~/.bashrc $ exec $SHELL -l $ anyenv install --init $ anyenv install nodenv $ exec $SHELL -l $ nodenv install 15.14.0 $ nodenv local 15.14.0 $ nodenv versions $ mkdir back-end //Serverless Fremework用のディレクトリを作成 $ cd back-end
Serverless Fremeworkを使ってAWS環境を構築します。
Serverless Fremeworkのプロジェクト作成
$ npm install serverless $ $(npm bin)/serverless create --template aws-nodejs --name serverless-backend
AWS Lambda、APIGateway、DynamoDB、IAM用にserverless.yamlを編集する
serverless.yaml
# Serverless Frameworkのプロジェクト名 service: backend # Serverless Frameworkのversion frameworkVersion: "2" provider: name: aws # 言語のversion(今回はnode.js) runtime: nodejs14.x lambdaHashingVersion: 20201221 # ここがAPIのパスになる stage: items region: ap-northeast-1 # Lambda関数を作成 functions: # 関数の名前を定義 backend: # lambda_handler名・APIGatewayを定義 handler: backend.handler name: ${self:service}-api events: - http: path: / method: post integration: lambda-proxy cors: headers: '*' - http: path: /{id} method: get integration: lambda-proxy cors: headers: '*' - http: path: /{id} method: delete integration: lambda-proxy cors: headers: '*' # LambdaにアタッチするIAMロールを選択(resourceでRoleの権限は別途定義して作成する) role: !GetAtt backendrole.Arn # IAMとDynamoDBを作成 # resources以下はCloudFormationと同じ記法が可能 resources: Resources: # IAMロールの名前を定義 backendrole: Type: AWS::IAM::Role # 以下でIAMロールを定義 Properties: # IAMロールの内容を定義 AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Action: - "sts:AssumeRole" Path: / Description: backend # IAMポリシーを作成 Policies: # IAMポリシーの名前 - PolicyName: backendPolicy # IAMポリシーの内容を定義 PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "sts:AssumeRole" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: "*" - Effect: "Allow" Action: - "dynamodb:DeleteItem" - "dynamodb:GetItem" - "dynamodb:PutItem" - "dynamodb:Scan" - "dynamodb:UpdateItem" Resource: "arn:aws:dynamodb:*:*:table/*" DynamoDbTable: Type: 'AWS::DynamoDB::Table' Properties: TableName: backend-items # キーの型を指定 AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5
Serverless Fremeworkを使って、Lambda関数を編集
編集する前にjsファイルの名前を変更してください。
$ mv handler.js backend.js
backend.js
const AWS = require("aws-sdk"); const dynamo = new AWS.DynamoDB.DocumentClient(); exports.handler = async (event, context) => { let body; let statusCode = 200; const headers = { "Content-Type": "application/json", }; console.log(JSON.stringify(event)); try { if ( event.requestContext.httpMethod == "DELETE" && event.requestContext.resourcePath == "/{id}" ) { await dynamo .delete({ TableName: "backend-items", Key: { id: event.pathParameters.id, }, }) .promise(); body = `Deleted item ${event.pathParameters.id}`; } else if ( event.requestContext.httpMethod == "GET" && event.requestContext.resourcePath == "/{id}" ) { body = await dynamo .get({ TableName: "backend-items", Key: { id: event.pathParameters.id, }, }) .promise(); console.log(body); } else if ( event.requestContext.httpMethod == "GET" && event.requestContext.path == "/items" ) { body = await dynamo .scan({ TableName: "backend-items" }) .promise(); } else if ( event.requestContext.httpMethod == "POST" && event.requestContext.path == "/items" ) { let requestJSON = JSON.parse(event.body); await dynamo .put({ TableName: "backend-items", Item: { id: requestJSON.id, name: requestJSON.name, }, }) .promise(); body = `Put item ${requestJSON.id}`; } else { throw new Error(`Unsupported route: "${event.routeKey}"`); } } catch (err) { statusCode = 400; body = err.message; } finally { body = JSON.stringify(body); } return { statusCode, body, headers: { "Access-Control-Allow-Headers": "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET", }, }; };
Serverless Fremeworkを実行
$ $(npm bin)/serverless deploy
CloudFormationがAWSリソースを作成する
作成が終われば、バックエンドの準備はOK AWSマネージメントコンソールから作成したサービスを確認する。
API Gatewayの画面を開いてURLをコピーして控えておきます。
ステージ>items>URL の呼び出し:https://{hogehoge}.execute-api.ap-northeast-1.amazonaws.com/items
ターミナルでAPIを叩いて確認してみましょう
$ curl -v \
-X POST \
-H "Content-Type: application/json" \
-d '{"id": "1","name": "hoge"}' \
https://{hogehoge}.execute-api.ap-northeast-1.amazonaws.com/items
DynamoDBのテーブルにデータが登録されているはずです。
frontendを構築
vue.jsの実行環境を作成します。
$ cd handson $ mkdir front-end //vue.js環境用のディレクトリを作成 $ cd front-end $ npm install vue $ npm install -g @vue/cli $ echo export PATH=$PATH:`npm bin -g` >> ~/.bash_profile $ source ~/.bash_profile $ vue --version $ vue init webpack front-end ? Project name front-end ? Project description A Vue.js project ? Author ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Set up unit tests No ? Setup e2e tests with Nightwatch? No ? Should we run `npm install` for you after the project has been created? (recommended ) npm $ npm install bootstrap bootstrap-vue $ npm install axios --save
main.js
import Vue from 'vue' import App from './App.vue' import router from './router' import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' //追加 import 'bootstrap/dist/css/bootstrap.css' //追加 import 'bootstrap-vue/dist/bootstrap-vue.css' //追加 Vue.use(BootstrapVue) //追加 Vue.use(IconsPlugin) //追加 Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app')
HelloWorld.vue
<template>
<div class='home'>
<b-container>
<b-row>
<b-col sm='12' class='mb-3'>
<hr>
</b-col>
<b-col sm='3' class='mx-auto mb-3'>
<b-form-input v-model='id' placeholder='id'></b-form-input>
<b-form-input v-model='name' placeholder='name'></b-form-input>
</b-col>
<b-col sm='12' class='mb-5'>
<b-button variant='primary' v-on:click='postData'>登録</b-button>
</b-col>
<b-col sm='3' class='mx-auto mb-3'>
<b-table striped hover :items='items'></b-table>
<b-form-input v-model='text' placeholder='id'></b-form-input>
</b-col>
<b-col sm='12' class='mb-5'>
<b-button variant='success' v-on:click='getData'>表示</b-button>
</b-col>
<b-col sm='12' class='mb-5'>
<b-button variant='danger' v-on:click='deleteData'>削除</b-button>
<hr>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'home',
components: {
},
data() {
return {
// API Gatewayの設定パス
path: 'https://{hogehoge}.execute-api.ap-northeast-1.amazonaws.com/items',
id: '',
name: '',
text: '',
items: []
}
},
methods: {
getData: function () {
// 検索ID指定
const path = this.path + '/' + this.text;
const myInit = {
headers: {},
response: true,
};
// データ取得
axios.get(path, myInit).then(response => {
// テーブル表示
console.log(response)
this.items = [
{
id: response.data.Item.id,
name: response.data.Item.name
}
];
}).catch(error => {
// テーブルリセット
this.items = [];
console.log(error)
});
},
postData: function () {
const myInit = {
headers: {},
response: true,
id: String(this.id),
name: String(this.name)
};
// データ登録
axios.post(this.path, myInit).then(response => {
console.log(response);
}).catch(error => {
console.log(error)
});
},
deleteData: function () {
const path = this.path + '/' + this.text;
const myInit = {
headers: {},
response: true,
};
// データ削除
axios.delete(path, myInit).then(response => {
console.log(response);
this.items = [];
});
}
}
}
</script>
※以下App.vueに関しては、編集しなくてもWEBページは表示させられます。
App.vue
<template> <div id="app"> <div id="nav"> <router-link to="/">従業員登録サイト</router-link> //変更 </div> <router-view/> </div> </template> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } #nav a.router-link-exact-active { color: #42b983; } </style>
vue.jsを実行
$ npm run dev
Open browserとポップが出てくるのでOpenをクリックすると以下の画面が表示されます。
※真っ白な画面が出てしまった場合は、どこか誤りがあります

API実行手順
1. id・nameを入力して登録ボタンを押す
DynamoDBのテーブルに入力したid・nameのデータが表示されます。

2. 先程作成したid・nameを入力して、表示ボタンを押す
DynamoDBのデータがWEBページに表示されます。
3. 削除ボタンを押す
DynamoDBからデータが削除されます。
お疲れさまでした!
これでWEBページからAPIを実行している挙動を確かめることができました。
確認ができたらAWSのリソースは以下で削除してしまいましょう。
serverless remove -v
以上、サーバーレスハンズオンでした。