ロゴテキスト ロゴ

    NodeでServerless Frameworkの開発環境を作る(ts版)

    NodeでServerless Frameworkの開発環境を作る(ts版)

    Node.js(TypeScript版)でServerless Frameworkの環境を作っていきます。

    検証した環境

    1 yarn 1.22.4
    2 serverless 2.8.0

    前準備

    プロジェクトの初期化を行うため serverless コマンドをグローバルで使えるようにインストールします

    # システム全体で使えるようにする
    $ npm i -g serverless
     
    # インストール出来た事の確認
    $ serverless -v
    Framework Core: 2.8.0
    Plugin: 4.1.1
    SDK: 2.3.2
    Components: 3.2.3


    serverless コマンドはインストールした時点で sls という短縮コマンドが使えるようになります

    # インストール時点で短縮コマンドが使える
    $ sls -v
    Framework Core: 2.8.0
    Plugin: 4.1.1
    SDK: 2.3.2
    Components: 3.2.3

    プロジェクトを初期化

    ここではNode.jsのTypeScript版で初期化します。
    私は「serverless-ts-sample」というフォルダにプロジェクトを作っていきます

    $ cd <プロジェクトフォルダ>
     
    # プロジェクトフォルダでServerless Frameworkの雛形を作るためのコマンドを実行
    $ sls create -t aws-nodejs-typescript

    このコマンドを実行するとフォルダ直下にファイルやフォルダがいくつか作られます

    serverless-ts-sample/
      .gitignore
      .vscode/
      handler.ts
      package.json
      README.md
      serverless.ts
      tsconfig.json
      webpack.config.js
    serverlessのファイル群が作られた

    プロジェクト初期化時のコマンドについて(余談)

    実行フォルダ直下にファイル群を作るか、フォルダの中にまとめた状態で作るか、で実行する $ sls create コマンドが変わってきます。( -p <プロジェクト名> を付与するかどうかの違い)


    例えば「hoge/」というフォルダで

    $ sls create -t aws-nodejs-typescript -p fuga

    と実行すると

    hoge/
      fuga/
        handler.ts
        package.json
        ・・・

    となり、


    $ sls create -t aws-nodejs-typescript

    と実行すると

    hoge/
      handler.ts
      package.json
      ・・・

    となります。

    プロジェクトに serverless を導入

    この時点だとプロジェクト内に serverless のパッケージが存在していません

    そのため以下のコマンドで追加します

    # プロジェクトにserverlessをインストール
    $ yarn add serverless
    ・・・
      Done in 10.68s.
     
    # プロジェクト内のserverlessのバージョン確認
    $ npx sls -v
    Framework Core: 2.8.0 (local)
    Plugin: 4.1.1
    SDK: 2.3.2
    Components: 3.2.3

    ここからはこのプロジェクト内にインストールした serverless を使っていきます。

    serverlessが実行出来る事の確認

    $ sls create をした時点で、エンドポイントを叩くとサンプルのJSONが返ってくる参考用の hello メソッドが用意されています。(細かい処理の流れの解説は省略)

    handler.ts
    import { APIGatewayProxyHandler } from 'aws-lambda'
    import 'source-map-support/register'
     
    export const hello: APIGatewayProxyHandler = async (event, _context) => {
      return {
        statusCode: 200,
        body: JSON.stringify(
          {
            message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!',
            input: event,
          },
          null,
          2
        ),
      }
    }

    まずそのメソッドが使えるかの確認をしてみます。
    メソッドを試すには $ sls invoke コマンドを使用します。

    # ローカルのsls invokeを使い hello メソッドを試す
    $ npx sls invoke local --function hello
     
    or
     
    # --function は -f と省略する事が可能
    $ npx sls invoke local -f hello

    実行すると webpack のバンドルが行われ、200のステータスコードが返ってくれば成功です!

    $ npx sls invoke local -f hello
    Serverless: Bundling with Webpack...
    Time: 79ms
    Built at: 10/17/2020 11:29:27 AM
         Asset      Size   Chunks             Chunk Names
    handler.js  6.29 KiB  handler  [emitted]  handler
    Entrypoint handler = handler.js
    [./handler.ts] 316 bytes {handler} [built]
    [source-map-support/register] external "source-map-support/register" 42 bytes {handler} [built]
    {
        "statusCode": 200,
        "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!\",\n  \"input\": \"\"\n}"
    }

    serverless invoke について

    Serverless Frameworkで作成するメソッドは、

    • エンドポイントを叩く
    • cronで決めた時間に実行する

    等、何かしらトリガーがあるはず、ですが、$ sls invoke はそれらのメソッドをすぐに実行し試す事が出来ます。



    もちろんローカルだけではなくデプロイしたものも実行出来ます。
    デプロイしたものは $ sls invoke hello とすればOK!

    $ npx sls invoke --help
    Plugin: Invoke
    invoke ........................ Invoke a deployed function
    invoke local .................. Invoke function locally
        --function / -f (required) ......... The function name
        --stage / -s ....................... Stage of the service
        --region / -r ...................... Region of the service
        --qualifier / -q ................... Version number or alias to invoke
        --path / -p ........................ Path to JSON or YAML file holding input data
        --type / -t ........................ Type of invocation
        --log / -l ......................... Trigger logging data output
        --data / -d ........................ Input data
        --raw .............................. Flag to pass input data as a raw string
        --app .............................. Dashboard app
        --org .............................. Dashboard org
        --config / -c ...................... Path to serverless config file

    Prettierを導入(お好み)

    Prettierを導入してルール決めしてしまえば、コーディングに集中しやすくなるので私はこの時点でPrettierを導入してます。

    $ yarn add -D prettier


    私の場合1人でプロジェクトを進める時はファイル数を増やしたくなくてPrettierの設定を基本的にpackage.jsonに書いてしまいますが、Prettierの設定内容や記載場所に関しては環境や好みに合わせて読み替えて下さい。

    package.json
    {
      "name": "serverless-ts-sample",
      ・・・
      "prettier": {
        "singleQuote": true,
        "semi": false,
        "printWidth": 100,
        "arrowParens": "avoid"
      }
    }

    この時点だとPrettierを効かせたいファイルがルートのみなので以下のコマンドを実行します

    $ npx prettier --write "./{*.ts,*.js}"
    handler.ts 153ms
    serverless.ts 10ms
    webpack.config.js 35ms

    serverless.ts → serverless.yml に

    ここもお好みになってしまうのですが、 aws-nodejs-typescript で作っているからなのか、最近の $ sls create がそうなのかは分からないのですが、 Serverless Frameworkの設定ファイルが serverless.yml ではなく serverless.ts で作られます


    「.yml」ファイルか「.ts」ファイルの差ですが、ネット上に上がっている記事が「.yml」で書かれているものがほとんどで、 かつ以前にプラグインを入れようとして「.ts」だと詰まってしまった事があり、私的に serverless.yml で書くのがおすすめです。



    そのため serverless.yml を追加し、 serverless.ts を削除します。

    serverless.ymlを追加

    serverless.yml の最小単位として以下のようになります

    serverless.yml
    # 自身のサービス名を記載
    service: serverless-ts-sample
     
    # webpackでバンドルするため serverless-webpack は必須
    plugins:
      - serverless-webpack
     
    # webpack.config.jsでexternalsを設定している場合、includeModules: true が必須
    # 設定しないとデプロイ後に Runtime.ImportModuleError というエラーが起きる
    custom:
      webpack:
        includeModules: true
     
    # awsを使用する事を明記
    # regionは使用したいリージョンを指定
    # 日本の場合、東京リージョンの ap-northeast-1 を利用する事が多いはず
    # runtimeには serverless.ts に書かれていたnodejsのバージョンを記載
    provider:
      name: aws
      region: ap-northeast-1
      runtime: nodejs12.x
     
    functions:
      hello:
        handler: handler.hello
        events:
          - http:
              path: hello
              method: get
     

    serverless.tsを削除

    「serverless.ts」ファイルを削除します。

    ファイルを削除しても $ sls invoke が実行出来る事を確認

    $ npx sls invoke local -f hello
    Serverless: Bundling with Webpack...
    ・・・
    {
        "statusCode": 200,
        "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!\",\n  \"input\": \"\"\n}"
    }

    処理を追加

    せっかくなので処理を追加してみます


    Serverless Frameworkの処理流れは大きく以下のようになります。

    • 1.serverless.yml に処理の設定を追加
      • 何をトリガーとするか?例えばエンドポイントを指定したAPIアクセスなど
      • その処理が呼ばれた時、実際の処理の呼び出し先の指定
    • 2.実際の処理を追加

    serverless.yml に処理の設定を追加

    処理の追加は functions の項目に行います。

    サンプルとして書かれている hello がとても分かりやすい!

    serverless.yml
    functions:
      # 処理の名前、invokeを使って呼び出す際に使用
      hello:
        # <ファイル名>.<そのファイル内のメソッド名>
        handler: handler.hello
        events:
          - http:
              # <エンドポイント>/helloで呼び出される
              # 仮に別項目で独自ドメイン(ex: hogehoge.com)を指定しデプロイした場合、
              # http://hogehoge.com/hello のようにして呼び出す事が可能
              path: hello
              method: get


    ↑を例に example という項目を作成してみます

    serverless.yml
    functions:
    ・・・
      example:
        handler: sample.example
        events:
          - http:
              path: sample
              method: get

    実際の処理を追加

    handler: sample.example としたので「sample.ts」に example メソッドを用意します

    sample.ts
    import { APIGatewayProxyHandler } from 'aws-lambda'
    import 'source-map-support/register'
     
    export const example: APIGatewayProxyHandler = async (_event, _context) => {
      console.log(_context)
     
      return {
        statusCode: 200,
        body: JSON.stringify(
          {
            message: 'hello Serverless Framework'
          },
          null,
          2
        ),
      }
    }

    console.log をしておくと $sls invoke した場合などに表示されます



    ここで最終的にreturnしている値は APIGatewayProxyResult として定義されています。
    statusCodebody は必須なんですね

    /**
     * Works with Lambda Proxy Integration for Rest API or HTTP API integration Payload Format version 1.0
     * @see - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
     */
    export interface APIGatewayProxyResult {
        statusCode: number;
        headers?: {
            [header: string]: boolean | number | string;
        };
        multiValueHeaders?: {
            [header: string]: Array<boolean | number | string>;
        };
        body: string;
        isBase64Encoded?: boolean;
    }


    追加したメソッドが使用出来るか試します

    $ npx sls invoke local -f example
    ・・・
    {
      invokeid: 'id',
      logGroupName: '/aws/lambda/serverless-ts-sample-dev-example',
      logStreamName: '2015/09/22/[HEAD]13370a84ca4ed8b77c427af260',
      functionVersion: 'HEAD',
      isDefaultFunctionVersion: true,
      functionName: 'serverless-ts-sample-dev-example',
      memoryLimitInMB: '1024',
      succeed: [Function: succeed],
      fail: [Function: fail],
      done: [Function: done],
      getRemainingTimeInMillis: [Function: getRemainingTimeInMillis]
    }
    {
        "statusCode": 200,
        "body": "{\n  \"message\": \"hello Serverless Framework\"\n}"
    }

    無事、200が返ってきました!

    AWSにデプロイ&削除

    最後にデプロイします。



    Serverless Frameworkの何がすごい!てここまでもすごいけど、
    デプロイもコマンド1個でデプロイ出来るんです!


    1個ですよ!
    $ npx sls deploy すれば、APIGatewayやLambda、S3等色んなところに勝手にデプロイしてくれます!
    なんだよ、神かよ、神なのかよ、、、

    AWSにデプロイ

    $sls deploy は -v--verbose )を付けると実行している内容の詳細が表示されるのでおすすめです。

    $ npx sls deploy -v
    {
      "webpackConfig": "webpack.config.js",
      "includeModules": false,
      "packager": "npm",
      "packagerOptions": {},
      "keepOutputDirectory": false
    }
    ・・・
    ・・・
    CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-ts-sample-dev
    CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
    CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
    CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
    CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - ExampleLogGroup
    CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
    CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::RestApi - ApiGatewayRestApi
    CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
    ・・・
    CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceHello
    CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
    CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
    CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - ExampleLambdaFunction
    ・・・
    ・・・
    Serverless: Stack update finished...
    Service Information
    service: serverless-ts-sample
    stage: dev
    region: ap-northeast-1
    stack: serverless-ts-sample-dev
    resources: 17
    api keys:
      None
    endpoints:
      GET - https://fghbzaelkd.execute-api.ap-northeast-1.amazonaws.com/dev/hello
      GET - https://fghbzaelkd.execute-api.ap-northeast-1.amazonaws.com/dev/sample
    functions:
      hello: serverless-ts-sample-dev-hello
      example: serverless-ts-sample-dev-example
    layers:
      None

    無事デプロイ出来ました!

    endpoints: に試しで追加した sample もあるのでアクセスしてみます。



    まずchromeでエンドポイントにアクセス

    chromeでデプロイしたエンドポイントにアクセス


    続いて $sls invoke

    $ npx sls invoke -f example
    {
        "statusCode": 200,
        "body": "{\n  \"message\": \"hello Serverless Framework\"\n}"
    }

    OKですね!

    AWSから削除

    削除もコマンド一発です!

    ゴッソリきれいにしてくれます、何から何まですごいなぁ、ほんと。


    こちらも同じく -v を付けておくと何をやっているかが見られて安心感があります

    $ npx sls remove -v
    Serverless: Getting all objects in S3 bucket...
    Serverless: Removing objects in S3 bucket...
    Serverless: Removing Stack...
    Serverless: Checking Stack removal progress...
    CloudFormation - DELETE_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-ts-sample-dev
    ・・・
    Serverless: Stack removal finished...

    これで削除も完了です!

    プロフィールの背景画像 プロフィール画像
    Yuki Takara
    都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。