TypeScriptでServerless FrameworkのPluginを書いてみる

2019.09.27

CX事業本部の夏目です。

Serverless FrameworkのPluginを作ったことがあるのですが、全然メンテできていません。

メンテしようと思うのですが、JavaScriptで書くのは辛いなぁということでTypeScriptで作り直そうと思います。

なので、今回はTypeScriptでServerless FrameworkのPluginを軽く書いてみようと思います。

参考資料

以下の資料を参考に進めていきます。

Plugin作成

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "strictBindCallApply": false,
    "outDir": "dist"
  }
}

ここで大事なこと - targetes6にすること - strictBindCallApplyfalseにすること - hookで動かす関数をしていする際によく書く方法でエラーが出ないようにするため

outDirはかならずしも必要ではないが、よく指定されているようなので自分も指定した。

package.json

{
  "name": "typescript_serverless_plugin_sample",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js",
  "scripts": {
    "build": "rm -rf dist && tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^12.7.8",
    "@types/serverless": "^1.18.2",
    "typescript": "^3.6.3"
  }
}

基本的にはnpm init -yで作成したものと同じ。
大事なのはmainの項目、JSにトランスパイルしたものを参照する必要があること。

src/index.ts

import * as Serverless from "serverless";

export default class Plugin {
  public serverless: Serverless;
  public option: Serverless.Options;
  public hooks: {
    [event: string]: Promise<any>;
  };

  constructor(serverless: Serverless, options: Serverless.Options) {
    this.serverless = serverless;
    this.option = options;

    this.hooks = {
      // tsconfig.jsonで "compilerOptions.strictBindCallApply == false" にしないと、トランスパイル時にここでエラーが起きる
      "before:package:createDeploymentArtifacts": this.run.bind(this)
    };
  }

  public async run() {
    this.serverless.cli.log('=== sample log ===');
  }
}

// Serverless FrameworkのPluginとして動作させるにはこう書かないといけない
//  書かないとうまく認識してもらえない
// ここの記述に "@types/node"が必要になる
module.exports = Plugin;

型の指定とかがあるだけで基本的に、JSで書くときと同じ。

試しに動かしてみる

準備

今まで作ったファイルと同じディレクトリに以下のファイルを置く。

serverless.yml

service: PluginTest

provider:
  name: aws
  runtime: nodejs10.x

plugins:
  localPath: '../' # これで今作成したPluginを読み込ませることができる
  modules:
    - typescript_serverless_plugin_sample

functions:
  hello:
    handler: handler.hello

handler.js

'use strict';

module.exports.hello = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Go Serverless v1.0! Your function executed successfully!',
        input: event,
      },
      null,
      2
    ),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

serverless.ymlにPluginの設定をいれただけで、あとはServerless Frameworkのテンプレートそのまま。

実行してみる

$ npm install serverless
$ npx sls package # 今回はpackageコマンドにhookを仕込んだのでpackageを実行する
Serverless: === sample log ===
Serverless: Packaging service...
Serverless: Excluding development dependencies...

Pluginが動きました。
TypeScriptでもServerless FrameworkのPluginを開発できそうです。

まとめ

TypeScriptでServerless FrameworkのPluginを作ってみました。
自分はこれを参考に以前作ったPluginのメンテナンスをしてみようと思います。

Serverless Frameworkで何か不都合があったときには、これでPluginを作ってみてください。