Serverless Componentを使ってexpressアプリをLambdaにデプロイする

## 概要

この記事は [AWS LambdaとServerless Advent Calendar 2020](https://qiita.com/advent-calendar/2020/lambda)の24日目の記事です。

今回は、Serverless Componentで、expressアプリをLambdaにデプロイしてみたので、手順をご紹介します。


## Serverless Componentとは


![serverless-component-logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2278/c0eff326-382d-da92-99fb-cac620290b29.png)

https://serverless.com/components/

Serverless Componentは、Serverless Frameworkを介して動作し、

開発者は、すでに提供されているコンポーネントを利用すれば、わずかな設定でアプリをデプロイできます。

今回は、serverless-component-expressというコンポーネントを利用して、expressアプリをデプロイします。

## Serverless Frameworkのインストール

以下のコマンドで、Serverless Frameworkをインストールします。

```bash
$ npm i -g serverless
```

serverlessコマンドが使えるようになります。Serverless Componentを使うためには、新しいバージョンを使う必要があります。
今回は、以下のバージョンで動作確認をしました。

```bash
$ npx serverless version
Framework Core: 2.15.0
Plugin: 4.2.0
SDK: 2.3.2
Components: 3.4.3
```


## expressアプリのデプロイ

以下のコマンドで、expressアプリのプロジェクトを作成します。


```bash
$ serverless init express-starter
$ cd express-starter
```

`app.js`は、expressで書かれたプログラムになっています。

```js:app.js
'use strict';

// eslint-disable-next-line import/no-unresolved
const express = require('express');

const app = express();

// Routes
app.get('/*', (req, res) => {
  res.send(`Request received: ${req.method} - ${req.path}`);
});

// Error handler
app.use((err, req, res) => {
  console.error(err);
  res.status(500).send('Internal Serverless Error');
});

module.exports = app;
```



プロジェクト内にある `serverless.yml` の内容は、以下のようになっています。

```yml:serverless.yml
app: express-starter
component: express
name: express-starter

inputs:
  src: ./
```

`component`で、`serverless-component-express`というコンポーネントを利用するように設定されています。
`inputs`で、Lambdaにデプロイするディレクトリを指定しています。

デプロイするためには、AWSにアクセスするための認証キーが必要にです。
`.env`ファイルで、認証キーを設定します。

```txt:.env
AWS_ACCESS_KEY_ID=xxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxx
```

以下のコマンドでデプロイします。

```bash
$ serverless deploy

serverless ⚡framework
Action: "deploy" - Stage: "dev" - Org: "memememomo" - App: "express-starter" - Name: "express-starter"

apiGatewayUrl: https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com
url:           https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com

Full details: ....

22s › Serverless › Success
```

`url`が出力されるので、このエンドポイントに対して、curlコマンドを実行して、動作確認をします。

```bash
$ curl https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com
Request received: GET - /
```

expressで書かれたとおりのレスポンスが返ってきますね。


## TypeScriptに対応してみる

expressアプリがデプロイできたのですが、これだと記事が一瞬で終わってしまうので、TypeScriptが使えるように設定してみます。

まず以下のコマンドで、TypeScriptとexpress用の型情報ファイルをインストールします。

```bash
$ npm i --save-dev typescript @types/express
```

エントリポイントとなる `app.js`を以下のような1行に書き換えて、TypeScriptからコンパイルされたJavascriptファイルを読み込むようにします。

```js:app.js
module.exports = require('./dist/app');
```

TypeScriptのコンパイル設定をするために、以下のような内容の`tsconfig.json`ファイルを作成します。


```json:tsconfig.json
{
  "compilerOptions": {
    "outDir": "./dist",
    "sourceMap": true,
    "module": "commonjs",
    "target": "es5",
    "moduleResolution": "node",
    "removeComments": true
  },
  "include": [
    "./src/**/*"
  ]
}
```

TypeScriptをコンパイルする環境が整いました。
さきほど動作させた `app.js` をTypeScriptで記述します。
以下のような内容になります。


```ts:src/app.ts
import * as express from 'express';

const app = express();

app.get('/*', (req, res) => {
    res.send(`TS Request received: ${req.method} - ${req.path}`);
});

app.use((err, res, f) => {
    console.error(err);
    res.status(500).send('Internal Serverless Error');
});

module.exports = app;
```

TypeScriptをコンパイルして、デプロイするコマンドを `package.json`の `scripts` に記述しておきます。

```json:package.json
{
  ...
  "scripts": {
    ...
    "deploy": "tsc && serverless deploy"
  },
  ...
}
```

以下のようなコマンドを実行すると、TypeScriptのコンパイルとデプロイ処理が実行されます。

```bash
$ npm run deploy
```

さきほどと同様に、以下のcurlコマンドで動作確認をしてみましょう。

```bash
$ curl https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com
Request received: GET - /
```


## 終わりに

サーバレス関連の便利なツールが徐々に増えてきましたね。
今後も期待大です。