見出し画像

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 が記載されていますのでそこにアクセスしてみましょう。

コメント 2020-05-09 191735

 Nest.js が生成した Hello World! が表示されていれば大成功です!🎉

 速度的にも 200ms といったところでしょうか。(ウォームスタートならば)

コメント 2020-05-09 191941

まとめ

 簡単に Nest.js + Serverless Framework の環境が構築でき、より快適な サーバサイド TypeScript 開発体験を得られました。

 今回の完成版はこちらに置いておきます。

 さぁ、手を動かすぞ!



😉