@vendia/serverless-express(旧aws-serverless-express)でExpressアプリをAPIGateway+Lambdaにデプロイしてみた

2022.02.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

吉川@広島です。

APIGateway+LambdaExpressアプリをデプロイできる@vendia/serverless-expressをデプロイして試してみました。

@vendia/serverless-expressについて

vendia/serverless-express: Run Node.js web applications and APIs using existing application frameworks on AWS #serverless technologies such as Lambda, API Gateway, Lambda@Edge, and ALB.

APIGateway+Lambda上でExpressを動かせるというライブラリです。

昔はaws-serverless-expressというパッケージがあってこちらが使われていた認識なのですが、@vendia/serverless-expressがその後継という形のようです。

AWS Serverless Express has moved On 11/30, the AWS Serverless Express library moved from AWS to Vendia and will be rebranded to @vendia/serverless-express. Similarly, the aws-serverless-express NPM package will be deprecated in favor of @vendia/serverless-express.

READMEにも「aws-serverless-expressから@vendia/serverless-expressに移行したよ」とありますね。

本手法のメリット/デメリット

【新機能】Amazon API Gatewayの設定方法にcatch-allパス変数、ANYメソッド、Lambdaとの新しいプロキシ連携の3機能が追加。 | DevelopersIO

@vendia/serverless-express+APIGatewayの {proxy+} パス と ANY メソッドですべてのルーティングを1つのLambda関数で捌く構成になります。これは以下のブログでもあるように、

CDKを使って、一つのLambda関数でAPI設計してみた | DevelopersIO

一つのLambda関数だけを用いた構成だと、Lambda関数が一つであるためデプロイの時間を短くすることができるというメリット

があります。

そして通常であれば、

複数のAPIを作るために一つのLambda関数内で、httpメソッド(GET,POST,PUT,DELETE)ごとに条件分岐を行い、さらにURLごとに条件分岐する必要があります。

という煩わしさが生じるデメリットがあるのですが、@vendia/serverless-expressを使えば馴染み深いExpressのルーティングを利用できるのでその短所も打ち消すことができます。

また、Expressアプリケーションなのでローカルサーバを起動しての動作確認が行いやすいこともメリットといえそうです。

気になる点を挙げるとすれば、王道のAPIGW+Lambda Wayとはいえず、ややHackyな手法ではあると思います。ExpressのようなWeb Application Frameworkを使うならECSやAppRunnerを使う方が素直な感じはします。それでもECSなどを使う場合と比べてLambdaにはゼロスケール1できるという強みがあるので、存外悪くない手法だと思います。

それではやっていきます。

環境

  • node 16.14.0
  • typescript 4.5.5
  • aws-cdk 2.12.0
  • express 4.17.2
  • @vendia/serverless-express 4.5.3

ソースコード全体

GitHubにアップロードしています。

cm-dyoshikawa/serverless-express-playground

Lambdaコード

// packages/server/src/index.ts

import serverlessExpress from '@vendia/serverless-express'
import express from 'express'
import cors from 'cors'

const app = express()
app.use(cors())
app.use(express.json())

app.get('/hello', (_, res) => {
  res.status(200).send({
    message: 'Hello serverless-express',
  })
})

app.get('/ping', (_, res) => {
  res.status(200).send({
    message: 'pong',
  })
})

export const handler = serverlessExpress({ app })

/hello/ping のルーティングを作ってみました。

CDKコード

AWS CDK v2で構成管理します。

import { Construct } from 'constructs'
import { Stack, StackProps, aws_lambda, aws_apigateway } from 'aws-cdk-lib'

export class IacStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    const fn = new aws_lambda.Function(this, 'fn', {
      code: aws_lambda.Code.fromAsset('../server/dist'),
      handler: 'index.handler',
      runtime: aws_lambda.Runtime.NODEJS_14_X,
      environment: {
        NODE_OPTIONS: '--enable-source-maps',
      },
    })
    const api = new aws_apigateway.RestApi(this, 'api', {
      defaultCorsPreflightOptions: {
        allowOrigins: aws_apigateway.Cors.ALL_ORIGINS,
        allowMethods: aws_apigateway.Cors.ALL_METHODS,
        allowHeaders: aws_apigateway.Cors.DEFAULT_HEADERS,
      },
    })
    api.root.addProxy({
      defaultIntegration: new aws_apigateway.LambdaIntegration(fn),
    })
  }
}

NODE_OPTIONS: '--enable-source-maps' はsourcemap対応です。下記をご参考ください。

TypeScript+Node.jsでsourcemap対応を加えてエラーログ調査を行いやすくしたい | DevelopersIO

IacStack のIacはInfrastructure as Codeのことでそれ以上の意味は特にありません。

addProxy() を使わずに addResource()addMethod() を使って下記のようにも書けるようでしたので併記しておきます。

-   api.root.addProxy({
-     defaultIntegration: new aws_apigateway.LambdaIntegration(fn),
-   })
+   api.root
+     .addResource('{proxy+}')
+     .addMethod('ANY', new aws_apigateway.LambdaIntegration(fn))

curlで叩いてみる

npm scriptsを組んでありますので npm run deploy でデプロイしてcurlで叩きます。

curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/hello
{"message":"Hello serverless-express"}
curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/ping 
{"message":"pong"}

意図通りの動作確認が取れました。

参考


  1. アクセスがない場合、起動インスタンスがゼロになることです。メリットはその間のクラウド課金を抑えられることが主なメリットです。こちらのブログなど参考になるかもしれません。[#ServerlessDays 2019]Zero Scale Abstraction in Knative Serving | DevelopersIO