Casual Developers Note

エンジニアやデザイナー向けの技術情報・英語学習情報・海外留学情報・海外旅行情報を提供中。世界を旅して人生を楽しもう。

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • お問い合わせ
現在の場所:ホーム / アーカイブserverless

2018年5月11日 By KD コメントを書く

Serverless FrameworkでサーバーレスなAPIアプリケーションを作ってみよう(API-Gateway + Lambda + DynamoDB)

Serverless FrameworkでサーバーレスなAPIアプリケーションを作ってみよう(API-Gateway + Lambda + DynamoDB)

AWSのLambdaを筆頭にサーバーレスアプリケーションが注目を集めています。その最大の利点は稼働しているサーバーの管理から解放されることです。しかも、安い。今回はServerless Frameworkを用いてサーバーレスなCRUDアプリケーションを作ってみましょう。

サーバーレスアーキテクチャとは?

サーバーレスアーキテクチャとは、AWSなどのプロバイダーにお金を払って、開発者が意識する必要のないサーバー(マネージドサービス)を手に入れ、それを使ったアプリケーションの設計の事です。

AWSなどのIaaSが出る以前は、物理サーバーを調達してインフラを構築し、その上にOSやミドルウェアをインストールし、やっとその上にアプリケーションを乗せて稼働させていました。お金がある企業は自社のデータセンターを持っていた時代です。それが、AWSの登場により、物理サーバーは不要となり、IaaSのプロバイダーからEC2インスタンスのようなサーバーのリソースを購入する形が主流となりました。そして、AWSのLambdaの登場により、サーバーのリソースを購入する必要が無くなり、ついには関数というアプリケーションの実行費用だけを払えば良くなりました。そのコストも非常に安価です。サーバーの管理から解放されるだけではなく、コスト削減にもなるのです。今後はサーバーレスアーキテクチャが主流になることは誰の目にも明らかです。

今回は、サーバーレスなアプリケーションをより簡単に実装できるServerless Frameworkを用いてTODOをCRUDする簡単なAPIアプリケーションを実装してみましょう。

今回のアプリケーションのアーキテクチャ

今回作るアプリケーションのアーキテクチャは以下です。

Aws lambda

サーバーレスアーキテクチャとしては基本的な構成ですが、API Gateway経由でLambdaの関数を呼び出し、DyanomoDBにデータを格納する構成です。Serverless Frameworkを使えばこれらがコマンドラインから楽々作成できます。

開発環境構築

それでは、事前準備から始めましょう。

前提条件

このチュートリアルを楽しむには以下の準備が必要です。

  • AWSのアカウント登録が完了している
  • Homebrewがインストール済みである
  • NodeJSがインストール済みである

細かいバージョンは「環境」を参照して下さい。

必要なCLIのインストール

Homebrewよりawscliをインストールします。(NPMよりaws-cliをグローバルでインストールしても良いです)

$ brew install awscli
$ aws --version
aws-cli/1.15.0 Python/3.6.5 Darwin/17.5.0 botocore/1.10.0

NPMよりServerless FrameworkのCLIをインストールします。

$ npm install -g serverless
$ sls --version
1.26.1

パッケージ管理はYarnを使うので、入れていない人はインストールしましょう。

$ npm install -g yarn
$ yarn --version
1.6.0

IAMのグループとユーザを作成する

一応、今回用にIAMのグループとユーザを作っておきましょう。すでに使えるユーザがある場合は作る必要はありません。

AWSコンソールにアクセスして、IMAの画面を開きます。

Aws iam12

まずはグループを作ります。メニューから「グループ」を選んで、「新しいグループの作成」をクリックします。

Aws iam11

任意のグループ名を入力して、「次のステップ」をクリックします。

Aws iam10

以下のポリシーを入力して、「次のステップ」をクリックします。

  • PowerUserAccess
  • IAMFullAccess

Aws iam9

後は「グループ作成」をクリックすればグループが作成されます。

グループ画面から「todos-developers」グループに適切なアクセス許可が設定されている事が確認できます。

Aws iam7

次にユーザを作ります。メニューから「ユーザー」を選び、「ユーザーを追加」をクリックします。

Aws iam6

任意のユーザ名を入力して、「次のステップ:アクセス権限」をクリックします。

Aws iam5

先程作ったグループを選択して、「次のステップ:確認」をクリックします。

Aws iam4

ユーザ名と所属しているグループを確認したら、「ユーザーの作成」をクリックします。

Aws iam2

これで、グループの作成とユーザの作成が完了しました。

Profileを設定する

コマンドラインから実行できるように、aws-cliを使って、作成したユーザの権限を設定します。

$ aws configure
AWS Access Key ID [****************OWKA]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [****************KTLq]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [us-east-1]: us-east-1
Default output format [json]: json
$ cat ~/.aws/credentials
[default]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

リージョンは普通は東京(ap-northeast-1)リージョンを選ぶと思いますが、チュートリアルなのでどこでも構いません。例として「バージニア北部(us-east-1)」で進めます。

Serverless Framework用の権限(profile)も設定します。

$ sls config credentials --provider aws --key YYYYYYYYYYYYYYYYYYYY --secret YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY --profile serverless-dev
Serverless: Setting up AWS...
Serverless: Saving your AWS profile in "~/.aws/credentials"...
Serverless: Success! Your AWS access keys were stored under the "serverless-dev" profile.
$ cat ~/.aws/credentials
[default]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[serverless-dev]
aws_access_key_id = YYYYYYYYYYYYYYYYYYYY
aws_secret_access_key = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

今回は同じIAMアカウントを使って問題ありませんが、実際に使う場合は権限を使い分けましょう。

これで事前準備は完了です。

TODOをCRUDするの簡単なAPIアプリケーションを作る

それではサクサクいきましょう。

プロジェクトの準備

今回のプロジェクト用のフォルダ、ファイル、パッケージを準備しましょう。

$ mkdir serverless-todos
$ cd serverless-todos
$ sls create -t aws-nodejs-ecma-script
$ yarn install
$ tree -L 1
.
├── first.js
├── node_modules
├── package.json
├── second.js
├── serverless.yml
├── webpack.config.js
└── yarn.lock 
$ rm first.js second.js
$ yarn add --dev serverless-offline serverless-dynamodb-local
$ mkdir api models utils
$ touch api/todoHandlers.js
$ touch models/Todo.js
$ touch utils/dynamoose.js
$ yarn add dynamoose uuid

dynamoose.jsを実装する

今回はORマッパーとしてDynamooseを使います。これは名前の通り、MongooseにインスパイアーされたDynamoDB用のORマッパーです。なので、NodeJSに馴染みのある人なら比較的使いやすい仕様になっています。

まずは、Profileを設定する共通ユーティリティを作りましょう。

import dynamoose from 'dynamoose';

if (process.env.NODE_ENV === 'production') {
  dynamoose.AWS.config.update({
    accessKeyId: 'YYYYYYYYYYYYYYYYYYYY',
    secretAccessKey: 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY',
    region: 'us-east-1'
  });
} else {
  dynamoose.AWS.config.update({
    region: 'localhost'
  });
  dynamoose.local('http://localhost:8000');
}

export default dynamoose;

より実用的には環境変数はプロパティに切り出すと良いでしょう。

Todo.jsを実装する

次に、モデルを作りましょう。

import uuid from 'uuid';
import dynamoose from '../utils/dynamoose';
const { Schema } = dynamoose;

const todoSchema = new Schema(
  {
    id: {
      type: String,
      hashKey: true,
      default: () => uuid.v4()
    },
    task: {
      type: String,
      required: true
    },
    completed: {
      type: Boolean,
      default: false
    }
  },
  {
    timestamps: true
  }
);

const Todo = dynamoose.model('todos', todoSchema);
export default Todo;

todoHandlers.jsを実装する

Lambda関数はHandlerとして実装しまましょう。

import Todo from '../models/Todo';

export const getTodos = (event, context, callback) => {
  Todo.scan()
    .exec()
    .then(todos =>
      callback(null, {
        statusCode: 200,
        body: JSON.stringify(todos)
      })
    )
    .catch(err =>
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          error: err
        })
      })
    );
};

export const getTodo = (event, context, callback) => {
  const { id } = event.pathParameters;

  Todo.get(id)
    .then(
      todo =>
        todo
          ? callback(null, {
              statusCode: 200,
              body: JSON.stringify(todo)
            })
          : callback(null, {
              statusCode: 404
            })
    )
    .catch(err =>
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          error: err
        })
      })
    );
};

export const addTodo = (event, context, callback) => {
  const body = JSON.parse(event.body);
  const { task } = body;
  if (!task) {
    return callback(null, {
      statusCode: 400,
      body: JSON.stringify({
        error: `The property "todo" is required.`
      })
    });
  }

  const newTodo = new Todo({ task });
  newTodo
    .save({ overwrite: false })
    .then(addedTodo =>
      callback(null, {
        statusCode: 200,
        body: JSON.stringify(addedTodo)
      })
    )
    .catch(err =>
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          error: err
        })
      })
    );
};

export const removeTodo = (event, context, callback) => {
  const { id } = event.pathParameters;

  Todo.delete({ id })
    .then(() =>
      callback(null, {
        statusCode: 204
      })
    )
    .catch(err =>
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          error: err
        })
      })
    );
};

export const updateTodo = (event, context, callback) => {
  const { id } = event.pathParameters;
  const { task, completed } = JSON.parse(event.body);
  let todo = {};
  if (task) todo.task = task;
  if (completed) todo.completed = completed;

  Todo.update({ id }, { $PUT: todo })
    .then(updatedTodo =>
      callback(null, {
        statusCode: 200,
        body: JSON.stringify(updatedTodo)
      })
    )
    .catch(err =>
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          error: err
        })
      })
    );
};

serverless.ymlを設定する

「serverless.yml」はServerless Framework用の設定ファイルです。以下のように設定しましょう。

service:
  name: serverless-todos

plugins:
  - serverless-webpack
  - serverless-dynamodb-local
  - serverless-offline

provider:
  name: aws
  runtime: nodejs8.10
  profile: serverless-dev
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'us-east-1'}
  apiKeys:
    - myApiKey
  environment:
    NODE_ENV: ${opt:env, 'development'}
    DYNAMODB_TABLE: todos
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"

package:
  include:
    - api/**
    - models/**
    - utils/**

functions:
  getTodos:
    handler: api/todoHandlers.getTodos
    events:
      - http:
          method: get
          path: todos
          private: true
  getTodo:
    handler: api/todoHandlers.getTodo
    events:
      - http:
          path: todos/{id}
          method: get
          private: true
  addTodo:
    handler: api/todoHandlers.addTodo
    events:
      - http:
          path: todos
          method: post
          private: true
  removeTodo:
    handler: api/todoHandlers.removeTodo
    events:
      - http:
          path: todos/{id}
          method: delete
          private: true
  updateTodo:
    handler: api/todoHandlers.updateTodo
    events:
      - http:
          path: todos/{id}
          method: patch
          private: true

resources:
  Resources:
    todosTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:provider.environment.DYNAMODB_TABLE}
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

custom:
  dynamodb:
    start:
      port: 8000
      inMemory: true
      migrate: true
      seed: true
    seed:
      test:
        sources:
          - table: ${self:provider.environment.DYNAMODB_TABLE}
            sources: [./seeds/fake-todos.json]

少し設定内容と使い方を説明します。

「profile」には今回作った「serverless-dev」を指定しています。省略すると「default」の権限を参照します。

「apiKeys」は、値を設定し、「functions」に「private: true」とすると、APIがプライベートになります。この意味は、APIを呼ぶ時にヘッダーに「x-api-key」が必要になります。今回は全てのAPIをプライベートにしています。

「package」は、「include」でビルドに含むディレクトリを記入します。逆に除外する場合は「exclude」を使用します。

「resources」にはAWS上に構築するDynamoDBとして「todosTable」を設定しています。

「custom」にはローカル実行用のDynamoDBの設定を記述しています。「inMemory: true」の場合は、ローカルで起動した時にデータがメモリに作成され、停止した時にデータは破棄されます。他にも「dbPath」や「sharedDb」を設定することで、ローカルにファイルとしてデータを残す事が可能です。「seed」にはテストデータのファイルを指定しています。

「${}」を使った設定は、optはコマンド引数、selfはこのファイル自体、envは環境変数を参照します。

ローカル環境でアプリケーションを実行する

テストデータを準備する

「serverless.yml」の「seed」に設定したテストデータを作成します。

$ mkdir seeds
$ touch seeds/fake-todos.json

「fake-todos.json」は以下のようにします。

[
  {
    "id": "3d1a0cd0-4aba-11e8-8541-13ccb3287721",
    "task": "Have a coffee",
    "completed": "false",
    "createdAt": 1524902442626,
    "updatedAt": 1524902442626
  },
  {
    "id": "42750d60-4aba-11e8-b86e-b3202a0b01c8",
    "task": "Go to work",
    "completed": "false",
    "createdAt": 1524902442626,
    "updatedAt": 1524902442626
  },
  {
    "id": "3eeaae20-4aba-11e8-8240-17e64d342d41",
    "task": "Go out for dinner",
    "completed": "false",
    "createdAt": 1524902442626,
    "updatedAt": 1524902442626
  },
  {
    "id": "3f9a7620-4aba-11e8-a3c1-61aaaadad74f",
    "task": "Get up early",
    "completed": "true",
    "createdAt": 1524902439922,
    "updatedAt": 1524902439922
  },
  {
    "id": "4025c630-4aba-11e8-ad79-5d5313a362c5",
    "task": "Make a ham and lettuce sandwich",
    "completed": "true",
    "createdAt": 1524902439922,
    "updatedAt": 1524902439922
  }
]

DynamoDB Localを動作確認する

アプリケーションを起動する前にDynamoDB Localをインストールしましょう。

$ sls dynamodb install
Started downloading Dynamodb-local. Process may take few minutes.
...
 Installation complete!

後でローカルのデータベースを削除する場合は「sls dynamodb remove」を実行すればできます。

次にオフラインでAPIを起動してみましょう。

$ sls offline start
...
Serverless: DynamoDB - created table todos
Seed running complete for table: todos
Serverless: Starting Offline: dev/us-east-1.
Serverless: Key with token: d41d8cd98f00b204e9800998ecf8427e
Serverless: Remember to use x-api-key on the request headers

Serverless: Routes for getTodos:
Serverless: GET /todos

Serverless: Routes for getTodo:
Serverless: GET /todos/{id}

Serverless: Routes for addTodo:
Serverless: POST /todos

Serverless: Routes for removeTodo:
Serverless: DELETE /todos/{id}

Serverless: Routes for updateTodo:
Serverless: PATCH /todos/{id}

Serverless: Offline listening on http://localhost:3000

「http://localhost:8000/shell/」にアクセスします。

Dynamodb 1

左側のパネルにDynamoDB Javascript Shellを入力することで実行できます。今回のアプリケーションのテーブル名は「todos」でした。テーブル内のデータを全て表示する以下のスクリプトを入力して実行してみましょう。

dynamodb.scan({ TableName: 'todos' }, (err, todos) => {
  if (err) return ppJson(err);
  ppJson(todos);
});

Dynamodb 2

テストデータが全て格納されている事が分かります。

GET /todos

ヘッダーに設定する「x-api-key」に設定する値は、起動時に表示された「token: d41d8cd98f00b204e9800998ecf8427e」を使います。

$ curl -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos | jq
...
[
  {
    "createdAt": "2018-04-28T08:00:39.922Z",
    "id": "3f9a7620-4aba-11e8-a3c1-61aaaadad74f",
    "task": "Get up early",
    "completed": true,
    "updatedAt": "2018-04-28T08:00:39.922Z"
  },
  {
    "createdAt": "2018-04-28T08:00:39.922Z",
    "id": "4025c630-4aba-11e8-ad79-5d5313a362c5",
    "task": "Make a ham and lettuce sandwich",
    "completed": true,
    "updatedAt": "2018-04-28T08:00:39.922Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3eeaae20-4aba-11e8-8240-17e64d342d41",
    "task": "Go out for dinner",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "42750d60-4aba-11e8-b86e-b3202a0b01c8",
    "task": "Go to work",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3d1a0cd0-4aba-11e8-8541-13ccb3287721",
    "task": "Have a coffee",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  }
]

GET /todos/{id}

$ curl -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos/3d1a0cd0-4aba-11e8-8541-13ccb3287721 | jq
...
{
  "createdAt": "2018-04-28T08:00:42.626Z",
  "id": "3d1a0cd0-4aba-11e8-8541-13ccb3287721",
  "task": "Have a coffee",
  "completed": false,
  "updatedAt": "2018-04-28T08:00:42.626Z"
}

POST /todos

$ curl -X POST -H 'Content-Type:application/json' -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' -d '{"task":"Add new todo"}' http://localhost:3000/todos | jq
...
{
  "message": "Added the todo of id:936407b0-4ac1-11e8-a4ba-c554c49eaa00",
  "addedTodo": {
    "id": "936407b0-4ac1-11e8-a4ba-c554c49eaa00",
    "task": "Add new todo",
    "completed": false,
    "createdAt": "2018-04-28T08:53:09.676Z",
    "updatedAt": "2018-04-28T08:53:09.676Z"
  }
}
$ curl -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos | jq
...
[
  {
    "createdAt": "2018-04-28T08:00:39.922Z",
    "id": "3f9a7620-4aba-11e8-a3c1-61aaaadad74f",
    "task": "Get up early",
    "completed": true,
    "updatedAt": "2018-04-28T08:00:39.922Z"
  },
  {
    "createdAt": "2018-04-28T08:00:39.922Z",
    "id": "4025c630-4aba-11e8-ad79-5d5313a362c5",
    "task": "Make a ham and lettuce sandwich",
    "completed": true,
    "updatedAt": "2018-04-28T08:00:39.922Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3eeaae20-4aba-11e8-8240-17e64d342d41",
    "task": "Go out for dinner",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:53:09.676Z",
    "id": "936407b0-4ac1-11e8-a4ba-c554c49eaa00",
    "task": "Add new todo",
    "completed": false,
    "updatedAt": "2018-04-28T08:53:09.676Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "42750d60-4aba-11e8-b86e-b3202a0b01c8",
    "task": "Go to work",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3d1a0cd0-4aba-11e8-8541-13ccb3287721",
    "task": "Have a coffee",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  }
]

DELETE /todos/{id}

$ curl -v -X DELETE -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos/3f9a7620-4aba-11e8-a3c1-61aaaadad74f
...
< HTTP/1.1 204 No Content
...
$ curl -v -X DELETE -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos/4025c630-4aba-11e8-ad79-5d5313a362c5 | jq
...
< HTTP/1.1 204 No Content
...
$ curl -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos | jq
...
[
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3eeaae20-4aba-11e8-8240-17e64d342d41",
    "task": "Go out for dinner",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:53:09.676Z",
    "id": "936407b0-4ac1-11e8-a4ba-c554c49eaa00",
    "task": "Add new todo",
    "completed": false,
    "updatedAt": "2018-04-28T08:53:09.676Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "42750d60-4aba-11e8-b86e-b3202a0b01c8",
    "task": "Go to work",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3d1a0cd0-4aba-11e8-8541-13ccb3287721",
    "task": "Have a coffee",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  }
]

PATCH /todos/{id}

$ curl -X PATCH -H 'Content-Type:application/json' -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' -d '{"completed":true}' http://localhost:3000/todos/3eeaae20-4aba-11e8-8240-17e64d342d41 | jq
...
{
  "createdAt": "2018-04-28T08:00:42.626Z",
  "completed": true,
  "id": "3eeaae20-4aba-11e8-8240-17e64d342d41",
  "task": "Add new todo",
  "updatedAt": "2018-04-28T09:07:34.272Z"
}
$ curl -X PATCH -H 'Content-Type:application/json' -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' -d '{"completed":true}' http://localhost:3000/todos/936407b0-4ac1-11e8-a4ba-c554c49eaa00 | jq
...
{
  "createdAt": "2018-04-28T08:53:09.676Z",
  "completed": true,
  "id": "936407b0-4ac1-11e8-a4ba-c554c49eaa00",
  "task": "Go out for dinner",
  "updatedAt": "2018-04-28T09:07:55.426Z"
}
$ curl -H 'x-api-key:d41d8cd98f00b204e9800998ecf8427e' http://localhost:3000/todos | jq
...
[
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "completed": true,
    "id": "3eeaae20-4aba-11e8-8240-17e64d342d41",
    "task": "Go out for dinner",
    "updatedAt": "2018-04-28T09:07:34.272Z"
  },
  {
    "createdAt": "2018-04-28T08:53:09.676Z",
    "completed": true,
    "id": "936407b0-4ac1-11e8-a4ba-c554c49eaa00",
    "task": "Add new todo",
    "updatedAt": "2018-04-28T09:07:55.426Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "42750d60-4aba-11e8-b86e-b3202a0b01c8",
    "task": "Go to work",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  },
  {
    "createdAt": "2018-04-28T08:00:42.626Z",
    "id": "3d1a0cd0-4aba-11e8-8541-13ccb3287721",
    "task": "Have a coffee",
    "completed": false,
    "updatedAt": "2018-04-28T08:00:42.626Z"
  }
]

ローカルで正しく動くことが確認できました。Control+Cでローカルで起動しているアプリケーションを停止しましょう。

また、このブログに載せているソースコードを転写する場合は基本的には動くはずですが、環境差分などですぐに動かない場合もありえます。その場合は先に次の章の「AWS上でアプリケーションを実行する」に進んで、後で戻ってデバッグすると理解が進むと思います。

AWS上でアプリケーションを実行する

いよいよ、AWS上にデプロイして実行しましょう。

AWSにデプロイする

$ sls deploy -s prod --env production
...
Service Information
service: serverless-todos
stage: prod
region: us-east-1
stack: serverless-todos-prod
api keys:
  myApiKey: fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf
endpoints:
  GET - https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos
  GET - https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/{id}
  POST - https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos
  DELETE - https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/{id}
  PATCH - https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/{id}
functions:
  getTodos: serverless-todos-prod-getTodos
  getTodo: serverless-todos-prod-getTodo
  addTodo: serverless-todos-prod-addTodo
  removeTodo: serverless-todos-prod-removeTodo
  updateTodo: serverless-todos-prod-updateTodo

AWS上にちゃんと作成されているか確認しましょう。

DynamoDBの画面を見ると、「todos」テーブルが作成されています。

Dynamodb 1

AWS Lambdaの画面を見ると、5つの関数が作成されています。

Lambda 1

関数を選択すると、以下のようにAPI Gateway、Lambda、DynamoDB、Cloudwatch Logsが自動的に作成されています。

Lambda 2

これが自動ですからお手軽ですね。

POST /todos

AWS上のDynamoDBは空なのでPOSTからいきましょう。

$ curl -X POST -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"task":"Add new todo 1"}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
{
  "message": "Added the todo of id:34eaa6d0-4acb-11e8-81a8-e5b9cc57316e",
  "addedTodo": {
    "id": "34eaa6d0-4acb-11e8-81a8-e5b9cc57316e",
    "task": "Add new todo 1",
    "completed": false,
    "createdAt": "2018-04-28T10:02:06.141Z",
    "updatedAt": "2018-04-28T10:02:06.141Z"
  }
}
$ curl -X POST -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"task":"Add new todo 2"}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
{
  "message": "Added the todo of id:3b3b03e0-4acb-11e8-81a8-e5b9cc57316e",
  "addedTodo": {
    "id": "3b3b03e0-4acb-11e8-81a8-e5b9cc57316e",
    "task": "Add new todo 2",
    "completed": false,
    "createdAt": "2018-04-28T10:02:16.734Z",
    "updatedAt": "2018-04-28T10:02:16.734Z"
  }
}
$ curl -X POST -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"task":"Add new todo 3"}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
{
  "message": "Added the todo of id:41b50900-4acb-11e8-81a8-e5b9cc57316e",
  "addedTodo": {
    "id": "41b50900-4acb-11e8-81a8-e5b9cc57316e",
    "task": "Add new todo 3",
    "completed": false,
    "createdAt": "2018-04-28T10:02:27.600Z",
    "updatedAt": "2018-04-28T10:02:27.600Z"
  }
}
$ curl -X POST -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"task":"Add new todo 4"}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
{
  "message": "Added the todo of id:7e6365e0-4acb-11e8-81a8-e5b9cc57316e",
  "addedTodo": {
    "id": "7e6365e0-4acb-11e8-81a8-e5b9cc57316e",
    "task": "Add new todo 4",
    "completed": false,
    "createdAt": "2018-04-28T10:04:09.407Z",
    "updatedAt": "2018-04-28T10:04:09.407Z"
  }
}
$ curl -X POST -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"task":"Add new todo 5"}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
{
  "message": "Added the todo of id:842e6650-4acb-11e8-81a8-e5b9cc57316e",
  "addedTodo": {
    "id": "842e6650-4acb-11e8-81a8-e5b9cc57316e",
    "task": "Add new todo 5",
    "completed": false,
    "createdAt": "2018-04-28T10:04:19.125Z",
    "updatedAt": "2018-04-28T10:04:19.125Z"
  }
}

GET /todos

$ curl -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
[
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:06.141Z",
    "task": "Add new todo 1",
    "id": "34eaa6d0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:06.141Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:04:09.407Z",
    "task": "Add new todo 4",
    "id": "7e6365e0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:04:09.407Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:27.600Z",
    "task": "Add new todo 3",
    "id": "41b50900-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:27.600Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:04:19.125Z",
    "task": "Add new todo 5",
    "id": "842e6650-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:04:19.125Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:16.734Z",
    "task": "Add new todo 2",
    "id": "3b3b03e0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:16.734Z"
  }
]

AWSのDynamoDBの「todos」テーブルのデータも確認してみましょう。

Dynamodb 2

POSTしたデータが入っていることが確認できます。

GET /todos/{id}

3番目に入れたデータを確認してみましょう。

$ curl -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/41b50900-4acb-11e8-81a8-e5b9cc57316e | jq
...
{
  "completed": false,
  "createdAt": "2018-04-28T10:02:27.600Z",
  "task": "Add new todo 3",
  "id": "41b50900-4acb-11e8-81a8-e5b9cc57316e",
  "updatedAt": "2018-04-28T10:02:27.600Z"
}

合っていますね。

DELETE /todos/{id}

4番目のデータを削除してみましょう。

$ curl -v -X DELETE -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/7e6365e0-4acb-11e8-81a8-e5b9cc57316e
...
< HTTP/1.1 204 No Content
...
$ curl -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
[
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:06.141Z",
    "task": "Add new todo 1",
    "id": "34eaa6d0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:06.141Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:27.600Z",
    "task": "Add new todo 3",
    "id": "41b50900-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:27.600Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:04:19.125Z",
    "task": "Add new todo 5",
    "id": "842e6650-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:04:19.125Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:16.734Z",
    "task": "Add new todo 2",
    "id": "3b3b03e0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:16.734Z"
  }
]

正しく削除されています。

一応AWS上のデータも確認しておきましょう。

Dynamodb 3

整合性は取れていますね。

PATCH /todos/{id}

1番目のデータと5番目のデータを更新しましょう。

$ curl -X PATCH -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"completed":true}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/34eaa6d0-4acb-11e8-81a8-e5b9cc57316e | jq
...
{
  "completed": true,
  "createdAt": "2018-04-28T10:02:06.141Z",
  "task": "Add new todo 1",
  "id": "34eaa6d0-4acb-11e8-81a8-e5b9cc57316e",
  "updatedAt": "2018-04-28T10:21:15.281Z"
}
$ curl -X PATCH -H 'Content-Type:application/json' -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' -d '{"completed":true}' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos/842e6650-4acb-11e8-81a8-e5b9cc57316e | jq
...
{
  "completed": true,
  "createdAt": "2018-04-28T10:04:19.125Z",
  "task": "Add new todo 5",
  "id": "842e6650-4acb-11e8-81a8-e5b9cc57316e",
  "updatedAt": "2018-04-28T10:21:41.415Z"
}
$ curl -H 'x-api-key:fd23sKJTVJY42xKBfd8nczlvsfucBKYF2o3GKYxf' https://v08cawefr9.execute-api.us-east-1.amazonaws.com/prod/todos | jq
...
[
  {
    "completed": true,
    "createdAt": "2018-04-28T10:02:06.141Z",
    "task": "Add new todo 1",
    "id": "34eaa6d0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:21:15.281Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:27.600Z",
    "task": "Add new todo 3",
    "id": "41b50900-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:27.600Z"
  },
  {
    "completed": true,
    "createdAt": "2018-04-28T10:04:19.125Z",
    "task": "Add new todo 5",
    "id": "842e6650-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:21:41.415Z"
  },
  {
    "completed": false,
    "createdAt": "2018-04-28T10:02:16.734Z",
    "task": "Add new todo 2",
    "id": "3b3b03e0-4acb-11e8-81a8-e5b9cc57316e",
    "updatedAt": "2018-04-28T10:02:16.734Z"
  }
]

これも一応、DynamoDBも確認してみましょう。

Dynamodb 4

OKですね。

AWSから削除する

これで動作確認は完了したので、不要になったアプリケーションを綺麗に削除しましょう。

$ sls remove -s prod
Serverless: Removing usage plan association...
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
.................................................................
Serverless: Stack removal finished...

AWS上に何も無いことを確認しましょう。

DynamoDBの画面には「todos」テーブルはありません。

Dynamodb 5

Lambdaの画面には今回デプロイした関数はありません。

Lambda 5

綺麗に削除されました。これでチュートリアルは終了です。

最後に

いかがでしたか?これでサーバーレスなアプリケーションを作れるようになった事でしょう。一度やってみると、案外簡単だと気づきますよね。マイグレーションでも新規開発でもサーバーレスアーキテクチャをどんどん採用していきましょう。では。

環境

  • OS: macOS High Sierra 10.13.4
  • Homebrew: 1.6.1
  • NodeJS: v9.8.0
  • Yarn: 1.6.0
  • serverless: 1.26.1
  • serverless-webpack: 3.1.1
  • serverless-offline: 3.20.3
  • serverless-dynamodb-local: 0.2.28
  • dynamoose: 0.8.7
  • uuid: 3.2.1

カテゴリ : 技術 Tips & Tutorials タグ : api-gateway, aws, dynamodb, lambda, nodejs, serverless, serverlessframework

ブログ更新情報や海外の関連情報などを配信する無料メルマガ

Sponsored Links

About Author

KD

世界を旅し日本を愛するエンジニア。大学でコンピュータサイエンスの楽しさを学び、日本の大手IT企業で働く中で、新しい技術やスケールするビジネスが北米にある事に気づく。世界に挑戦するための最大の壁が英語であったため、フィリピン留学およびカナダ留学を経て英語を上達させた。現在は日本在住でエンジニアとして働きつつ、次の挑戦に備えて世界の動向を注視している。挑戦に終わりはない。このブログでは、エンジニアやデザイナー向けの技術情報から、海外に留学したい人向けの留学情報、海外に興味がある人向けの海外旅行情報など、有益な情報を提供しています。

https://casualdevelopers.com/

最近の投稿

  • 2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年1月13日
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    2019年11月4日
  • ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    2019年10月30日
  • BashからZshに移行する方法(Mac編)

    BashからZshに移行する方法(Mac編)

    2019年10月21日
  • Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    2019年9月30日

カテゴリ

  • 技術 Tips & Tutorials (100)
  • 技術塾 (6)
  • ライフハック (26)
  • 海外留学 (12)
  • 英語学習 (3)
  • コラム (6)

アーカイブ

最高の学習のために

人気記事ランキング

  • MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
    MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
  • Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
    Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
  • SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
    SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
  • Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
    Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
  • [tips][perl] Perlで文字コードをいい感じに処理する方法
    [tips][perl] Perlで文字コードをいい感じに処理する方法
  • 爆速でJenkinsをマスターしよう(GitHubアカウント統合編) ~ JenkinsのGitHub Organizationの設定方法 ~
    爆速でJenkinsをマスターしよう(GitHubアカウント統合編) ~ JenkinsのGitHub Organizationの設定方法 ~
  • FacebookログインのJavascriptのSDKをサクッと試す方法
    FacebookログインのJavascriptのSDKをサクッと試す方法
  • PythonでWebスクレイピング入門(Scrapy+Selenium編)
    PythonでWebスクレイピング入門(Scrapy+Selenium編)
  • Amazon EC2インスタンスにSSHできなくなった時の対処法
    Amazon EC2インスタンスにSSHできなくなった時の対処法
  • [tips][bat] バッチで明日の日付を計算する。
    [tips][bat] バッチで明日の日付を計算する。

Bitcoin寄付 / BTC Donation

Bitcoinを寄付しよう

BTC
Select Payment Method
Personal Info

Donation Total: BTC 0.0010

このブログの運営のためにBitcoinでの寄付を募集しています。お気持ち程度の寄付を頂けると管理者の励みになります。

Bitcoin寄付について知りたい方はこちらの記事へ

ビットコイン取引ならここ

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • サイトマップ
  • タグ一覧
  • プライバシーポリシー
  • お問い合わせ

Copyright © 2023 KD - Casual Developers Notes