神
Getting Started With Serverless and TypeScript
用意するもの
- Node.js
- あんまり古くないやつ
Hello World まで
repo を作る
$ mkdir oreno-sls-ts $ cd oreno-sls-ts $ git init
依存物をインストール
$ npm init $ npm install --save-dev serverless serverless-webpack ts-loader tslint typescript webpack
今回はあえて serverless
をグローバルに入れないパティーンでやってみる。package.json
でserverless
のバージョンを管理できるメリットがある(serverless.yml
の中にバージョン固定する設定もできるけど)。
雛形を作り、いらないものを一旦捨てる
$ node_modules/.bin/serverless create --template aws-nodejs $ node_modules/.bin/tslint --init $ rm handler.js serverless.yml # このあと手動でつくる
いろいろ作る
.gitignore
node_modules .serverless .built
serverless.yml
service: oreno-sls-ts provider: name: aws runtime: nodejs4.3 plugins: - serverless-webpack functions: hello: handler: index.hello events: - http: path: hello method: get
tsconfig.json
{ "compilerOptions": { "target": "es5", "module": "commonjs" }, "exclude": [ "node_modules" ] }
webpack.config.js
var path = require('path'); module.exports = { entry: './index.ts', target: 'node', module: { loaders: [ { test: /\.ts(x?)$/, loader: 'ts-loader', }, ], }, resolve: { extensions: [ '.ts', '.js', '.tsx', '.jsx', '', ], }, output: { libraryTarget: 'commonjs', path: path.join(__dirname, '.built'), filename: 'index.js', }, };
index.ts
export * from './functions/hello';
functions/hello.ts
export function hello(event, context, callback): void { const response = { statusCode: 200, headers: { }, body: JSON.stringify({ "message": "Hello TypeScript!!!!" }) }; callback(null, response); };
ローカルで動作確認
$ node_modules/.bin/serverless webpack serve
うまく動いていればhttp://localhost:8000/helloで動作確認できるはず。
デプロヰ
$ node_modules/.bin/serverless deploy
$ curl https://********.execute-api.us-east-1.amazonaws.com/dev/hello | jq
うまくいけば {"message": "Hello TypeScript!!!!"}
が表示されるはず。
せっかくの TypeScript なので
型定義を使ってみる
Lambda Function に渡される context
オブジェクトを型定義してみます。 event
はめちゃくちゃ長くてアレだったので、ちゃんと書くときには定義します…
types.d.ts
export interface LambdaExecutionContext { callbackWaitsForEmptyEventLoop: boolean logGroupName: string logStreamName: string functionName: string memoryLimitInMB: string functionVersion: string invokeid: string awsRequestId: string invokedFunctionArn: string }
さっきの hello
function を少し変更してこれを使ってみます。
functions/hello.ts
import {LambdaExecutionContext} from '../types'; export function hello(event, context: LambdaExecutionContext, callback): void { const response = { statusCode: 200, headers: { }, body: JSON.stringify({ "message": `Hello!!!! I am a function named "${context.functionName}"` }) }; callback(null, response); };
さてさて型定義されているということは。 Visual Studio Code なら補完が効きますし、
ありえないプロパティを指定するとちゃんとエラーを指摘してくれます。
すてき!!!!!
AWS SDK で async/await
してみる
パッケージを追加する。
$ npm install --save-dev aws-sdk @types/aws-sdk json-loader
@types/aws-sdk
はDefinitelyTyped にある AWS SDK 用の型定義。 TypeScript の型定義管理はちょうざっくり変遷を辿ると
1.tsd
っていう型定義管理ツールがあるぞ
2.tsd
はオワコン。 typings
の時代だぜ
3.型定義インストール専用のツールなんてもういらん。npm
で@types/foo
って入れればおk
…という流れらしく、従ってnpm
で入れる。
json-loader
は AWS SDK の中で require('foo.json')
してるの(たぶん)を Webpack で処理するのに必要。
してるの(たぶん)を Webpack で処理するのに必要。
ちょっと昔は AWS SDK と Webpack の組み合わせに問題があり、みんな困ってたけど直った模様。マジ感謝。
index.ts
export * from "./functions/hello"; export * from "./functions/listfunctions";
functions/listfunctions.ts
import * as AWS from "aws-sdk"; import {LambdaExecutionContext} from "../types"; const lambda = new AWS.Lambda(); export async function listfunctions(event, context: LambdaExecutionContext, callback): Promise{ const functions = []; let nextMarker = null; do { const response: AWS.Lambda.ListFunctionsResponse = await lambda.listFunctions({Marker: nextMarker}).promise(); functions.push(...response.Functions); nextMarker = response.NextMarker; } while (nextMarker); callback(null, { statusCode: 200, headers: {}, body: JSON.stringify({functions}), }); };
を追加します。 AWS の API によくあるNextMarker
での判断を含んだ非同期 N 回ループがこんなに簡単に書けちゃいます。最高。
他のファイルも微変更が必要なのですが、後述の repo に全部あげておくのでそちらを参照してください。
再度デプロイして…
$ node_modules/.bin/serverless deploy
実行してみます。
$ curl https://********.execute-api.us-east-1.amazonaws.com/dev/listfunctions | jq
うまくいけばデプロイされたこれ自体を含む Lambda Function の一覧が取れるはずですね!
ここまでやったのがこちら
どうぞご笑覧ください。その後、色々を見直してこの記事の内容と結構違いがあったりします(index.js
を置かないようにした、とか)ので、ご注意を。