Nest.js + Serverless Framework + TypeScript で API Gateway + Lambda 環境を構築してみる
はじめに
Serverless Framework + TypeScript はいいぞ!
と思ってますが、 serverless create -t aws-nodejs-typescript を叩いて生成されるプロジェクトがシンプル過ぎて、少し複雑な事やろうとすると、「ディレクトリ構成どうしよう」「どのように分割しよう」「RDS と接続するにはどうすんの」「マイグレーションどうしよう」のように悩みどころが多く、Serverless Framework の上に別のフレームワーク欲しいと思ってしまっていました。
ここ数週間で Nest.js を知り、上記を理由に作ろうとしている自作 Web アプリのバックエンド側に Serverless Framework ではなく Nest.js を使ってみようと考えていました。(それに合わせて実行環境は Fargate にしてみよう。勉強しようとも思ってました)
(DynamoDB の知見ゼロで RDS 使う前提のためこんな結論になりました)
と思っていた矢先、なんとなく「Serverless Framework Nestjs」で検索してみたら、なんということでしょう。Serverless Framework + Nest が実現できるではありませんか! 先人達に感謝!🙏
というわけで、やってみた!
Nest.js プロジェクトの作成
npx nest new example-serverless-nestjs
パッケージマネージャについて聞かれます。Serverless Framework が npm っぽいのでそちらに合わせて npm を選択しました。
? Which package manager would you ❤️ to use? (Use arrow keys)
npm
Nest.js プロジェクト作成はこれで完了です。 VS Code でプロダクトを開きましょう。
code ./example-serverless-nestjs
Serverless Framework 設定
必要なパッケージをインストールします。
npm i -S aws-lambda aws-serverless-express express
npm i -D @types/aws-serverless-express serverless-layers
この時前に書いた serverless-offline を導入してもいいと思います。
次に Lambda ハンドラを作成します。
src/handler.ts
import { Context, Handler } from 'aws-lambda';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Server } from 'http';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as serverless from 'aws-serverless-express';
import * as express from 'express';
let cachedServer: Server;
async function bootstrapServer(): Promise<Server> {
const expressApp = express();
const adapter = new ExpressAdapter(expressApp);
const app = await NestFactory.create(AppModule, adapter);
app.enableCors();
await app.init();
return serverless.createServer(expressApp);
}
export const handler: Handler = (event: any, context: Context) => {
if (!cachedServer) {
bootstrapServer().then(async server => {
cachedServer = server;
return serverless.proxy(server, event, context, 'PROMISE').promise;
});
}
return serverless.proxy(cachedServer, event, context, 'PROMISE').promise;
};
ソースコードは準備できましたので、serverless.yml を作成します。
serverless.yml
サービス名だったり、デプロイ用バケットなどは適宜変更してください。
service: example-serverless-nestjs
plugins:
- serverless-layers
provider:
name: aws
runtime: nodejs12.x
region: ap-northeast-1
memorySize: 256
logRetentionInDays: 1
apiGateway:
minimumCompressionSize: 1024
environment:
AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1
LANG: ja_JP.UTF-8
versionFunctions: false
deploymentBucket:
name: dafujii-serverless-deploymentbucket
package:
individually: true
include:
- dist/**
exclude:
- "**"
functions:
index:
handler: dist/handler.handler
events:
- http:
cors: true
path: "/"
method: any
- http:
cors: true
path: "{proxy+}"
method: any
ついでに package.json 内の scripts にデプロイ用スクリプトも定義しておくとデプロイが楽になるでしょう。
package.json (scripts 内に追記)
"deploy:dev": "nest build && serverless deploy",
"deploy:prod": "nest build && serverless deploy --stage prod"
デプロイ!
デプロイ用スクリプトは仕込んでますのでコマンド1つで済ませられます!
npm run deploy:dev
ログが流れていきますので筋トレしながら待ちましょう。
Serverless: [ LayersPlugin ]: => default
... ○ Downloading package.json from bucket...
... ○ package.json does not exists at bucket...
... ○ Changes identified ! Re-installing...
... ∅ [warning] ".npmrc" file does not exists!
... ∅ [warning] "yarn.lock" file does not exists!
> @nestjs/core@7.0.9 postinstall S:\serverless\example-serverless-nestjs\.serverless\layers\nodejs\node_modules\@nestjs\core
> opencollective || exit 0
added 152 packages from 156 contributors and audited 273206 packages in 8.774s
5 packages are looking for funding
run `npm fund` for details
found 1 low severity vulnerability
run `npm audit fix` to fix them, or `npm audit` for details
... ○ Created layer package S:\serverless\example-serverless-nestjs\.serverless\example-serverless-nestjs-dev-nodejs-default.zip (14.5 MB)
... ○ Uploading layer package...
... ○ OK...
... ○ New layer version published...
... ○ Uploading remote S:\serverless\example-serverless-nestjs\package.json...
... ○ OK...
... ○ Adding layers...
... ✓ function.index - arn:aws:lambda:ap-northeast-1:*********:example-serverless-nestjs-dev-nodejs-default:1
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service index.zip file to S3 (49.5 KB)...
Serverless: Validating template...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
...................................
Serverless: Stack create finished...
Service Information
service: example-serverless-nestjs
stage: dev
region: ap-northeast-1
stack: example-serverless-nestjs-dev
resources: 11
api keys:
None
endpoints:
ANY - https://5g5isdtma9.execute-api.ap-northeast-1.amazonaws.com/dev/
ANY - https://5g5isdtma9.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
functions:
index: example-serverless-nestjs-dev-index
layers:
None
Serverless: [ LayersPlugin ]: => Layers Info
... ○ function.index = layers.arn:aws:lambda:ap-northeast-1:*********:example-serverless-nestjs-dev-nodejs-default:1
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
やりました。
ブラウザで確認!
デプロイ時に吐かれているログに URL が記載されていますのでそこにアクセスしてみましょう。
Nest.js が生成した Hello World! が表示されていれば大成功です!🎉
速度的にも 200ms といったところでしょうか。(ウォームスタートならば)
まとめ
簡単に Nest.js + Serverless Framework の環境が構築でき、より快適な サーバサイド TypeScript 開発体験を得られました。
今回の完成版はこちらに置いておきます。
さぁ、手を動かすぞ!
😉