botchy hack note

TypeScript、Ruby、Golang とか Macとかの備忘録です.間違いがあるとご指摘くださると嬉しいです.

AWS Lambda のファンクションを TypeScript で作る! 入門編

f:id:bokken31:20170201224015p:plain

今回は TypeScript シリーズ第2弾。 AWS Lambda のファンクションを TypeScript 作成する方法について。

最近、サーバーレス化、Micro Services 化の流れで、AWS Lambda を使用する機会が増えてきてるように思います。

煩わしいサーバの管理から解放されてサービスのみに注力できるのはかなり魅力的。今回は Node.js と TypeScript を使って、Lambda ファンクションを作成する方法について解説していきます。

目標は AWS Lambda を使って CloudWatch Logs (ログ) に 天気予報を出力するところまで。

天気予報の取得には お天気Webサービス仕様 - Weather Hacks - livedoor 天気情報 を利用します。実際に取ってくるのはサンプルにある福岡県久留米の天気情報です。(http://weather.livedoor.com/forecast/webservice/json/v1?city=400040)

目次

開発環境導入

Node.js と yarn (パッケージ管理コマンド) の導入

  • brewyum、apt-get で入れるもの

    • nodebrew (複数バージョンの Node.js を導入できるように)
    • yanr (npm の代替ソフト)
  • brew の場合 (Mac)

brew install nodebrew yarn
echo "PATH=$HOME/.nodebrew/current/bin" >> ~/.bashrc
  • apt-get の場合 (Ubuntuなど)
apt-get install nodebrew yarn
echo "PATH=$HOME/.nodebrew/current/bin" >> ~/.bashrc

そして、下記コマンドで Node.js 4.3 をインストールして使用するバージョンを 4.3.2 とします。

nodebrew install-binary 4.3.2
nodebrew use 4.3.2

もしここまでで何かしらのエラーがでたら、nodebrew のディレクトリがないことが原因かもしれません。 なので下記コマンドを実行してサイドnodebrew install-binary を実行し直してみてください。

mkdir -p ~/.nodebrew/src

Lambda ファンクション作成準備

Lambda プロジェクト用のディレクトリを作成する。 今回は天気予報の情報を出力する Lambda なので、weather-lambda とでもする。

mkdir weather-lambda
cd weather-lambda

その後、下記コマンドでプロジェクトの初期化を行う。

yanr init

質問事項を聞かれますが、今回は全部 Enter で OK です。

すると、下記のような package.json が生成されています。

{
  "name": "weather-lambda",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}

次節に示すパッケージのインストールを行うと、ここにプロジェクトに必要なパッケージ名などが記載されていきます。

必要なパッケージの導入

今回インストールするものは下記4種類です。

  • typescript
  • request
  • @types/node
  • @types/request

インストールの際は下記のコマンドでパッケージをインストールする。

yanr add -D typescript @types/node @types/request
yarn add request

TypeScript の設定ファイルを生成

TypeScript のコンパイラオプションを指定するために tsconfig.json を下記コマンドで作成する。

./node_modules/.bin/tsc --init

すると、下記の様な tscofnig.json が生成される。

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": false
    }
}

念のため上記のオプションの説明すると

  • module
    • モジュールコードの生成方法の指定 (“None”, “CommonJS”, “AMD”, “System”, “UMD”, “ES6”, or “ES2015”)
  • target
    • ECMAScript のバージョン(“ES3” (default), “ES5”, “ES6”/“ES2015”, “ES2016”, “ES2017” or “ESNext”. )
  • noImplicityAny
    • 暗黙的に any を使用していたらエラーを出すかどうか
  • sourceMap
    • ソースマップファイルを生成するかどうか

もっとコンパイラオプションに凝りたい方はコンパイラオプションのドキュメントを参照してみてください。

TypeScript によるファンクションの実装

実装は下記の通り。

単純に request を使ってお天気Webサービスから情報を GET してきて console.log でログに吐き出しています。 Lambda では最後に callback を呼ぶのをお忘れなく。

失敗のときは callback の第一引数にエラーを表す文字やエラーオブジェクトを、成功のときは第一引数は null で第二引数は成功のときの文字を渡します。

import * as request from 'request';

exports.handler = (event: any, context: any, callback: Function) => {
  const url: string = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=400040';
  request(url, (err: Error, res: request.RequestResponse, body: any) => {
    // Return if Error
    if(err || res.statusCode !== 200) { return callback(err); }

    // Parse JSON file (body)
    let jsonFile: any = JSON.parse(body);
    console.log(jsonFile.title);
    console.log(jsonFile.description);

    return callback(null, 'Success!');
  });
}

Lambda にアップロードできる形に変換

次にTypeScript のソースを JavaScript にトランスパイルします。そのときに使うのは tsc*1 コマンド。

インストールしたパッケージのうち bin があるものは ./node_modules/.bin 以下に保存されるのでそちらから実行する。

./node_modules/.bin/tsc index.ts

すると、下記のような index.js ファイルが生成される。

"use strict";
var request = require("request");
exports.handler = function (event, context, callback) {
    var url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=400040';
    request(url, function (err, res, body) {
        // Return if Error
        if (err || res.statusCode !== 200) {
            return callback(err);
        }
        // Parse JSON file (body)
        var jsonFile = JSON.parse(body);
        console.log(jsonFile.title);
        console.log(jsonFile.description);
        return callback(null, 'Success!');
    });
};

zip 化

では、Lambda にアップロードするために生成された js ファイルとライブラリを zip にまとめます。ディレクトリ上で下記コマンドを実行。

zip -r lambda.zip *

こうしてできた lambda.zipAWS Lambda にアップロードします。

AWS Lambda 上へのアップロード

まず、AWS コンソールにアクセスして Lambda のページにアクセスします。

f:id:bokken31:20170201230044p:plain

そして、Create Function を選択します。

f:id:bokken31:20170206224228p:plain

次に、Blanc Function を選択して Next をクリックします。

f:id:bokken31:20170206224345p:plain

それからトリガーの画面です。ここでは S3 のオブジェクトの更新や、1分間隔のイベントなどを Lambda の起動のトリガーに設定することができます。 今回は、サンプルなので特に何も設定せずに Next をクリックします。

f:id:bokken31:20170206224448p:plain

次に Lambda ファンクションの設定画面です。

f:id:bokken31:20170206225458p:plain

ここでは、Lambda ファンクションの名前、Runtime (実行する環境)、Code の指定方法を選択します。 ファンクションの名前は今回は天気予報の結果を取得してくるので、weatherLambda としました。それから実行環境は Node.js 4.3 で、 Code entry type を Upload a .ZIP file にして、Upload ボタンをクリックします。 そしたら、ファイルダイアログが出てくるので先程 zip 化したファイルを選択します。

次に、Lambda を実行するための権限を作成します。今回は単純に AWS Lambda を実行するだけなので、Role の Create a custom role を選択します。

f:id:bokken31:20170206230335p:plain

すると下記のような別画面に遷移します。ただ特にすることもなく、ロール名が lambda_basic_execution になっていれば許可をクリックします。

f:id:bokken31:20170206230432p:plain

すると、Role が existing になって、Existing role が lambda_basic_execution になっているはずです。

f:id:bokken31:20170206230943p:plain

以上で設定は終わりなので、Next ボタンをクリックします。

f:id:bokken31:20170206225525p:plain

すると、緑の枠に囲われて Conguraturations! が表示されるので、動作確認のためTest をクリックします。

f:id:bokken31:20170206231117p:plain

次に、Input test event を選択します。ここでは、TypeScript で記述したハンドラのexports.handler = (event: any, context: any, callback: Function) => {の event に何を渡して実行するか?を選択します。今回は event は使っていないので何でも大丈夫です。ひとまずHello Worldを選択しましょう。

f:id:bokken31:20170206231404p:plain

すると、下記のようにSuccess! と log output が表示されます。ここでは少し出力結果が見にくいので click here をクリックして CloudWatch log でログを確認してみましょう。

すると下記のような画面に遷移するはずなので、ログをクリックしましょう。*2

f:id:bokken31:20170206231959p:plain

すると下記のように詳細なログを確認することができます。

f:id:bokken31:20170206232212p:plain

ログを確認してみると、福岡県久留米の天気と表示されていることや福岡県の天気が表示されていることが確認できるはずです。

以上までが TypeScript で作る簡単な Lambda のサンプルアプリケーションです。

おわりに

今回はサンプルとして、簡単なアプリケーションを Lambda と TypeScript を使って作成しました

TypeScript なのにあまり型を活かせていなかったり、タスクランナーの説明やらを省いてしまいましたが、そのあたりはまた別記事で紹介をしていきたいと思います。

ひとまず、TypeScript でラムダファンクションを作ってみたいという方の導入になれば嬉しいです。

*1:TypeScript Compiler

*2:下記画像は何度か試したので幾つかログがありますが、一回しか Test を実行していなければログは一つだけのはずです。