Nest.jsをCloud Functionsで動かすメモ。
まずはよく見かけるサンプルコード
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as express from 'express';
import * as functions from 'firebase-functions';
import { AppModule } from './app.module';
const server = express();
(async () => {
const app = await NestFactory.create(AppModule, new ExpressAdapter(server));
app.enableCors();
return app.init();
})();
export const api = functions.https.onRequest(server);
Cloud Functionsはデプロイ時にエントリポイントとなるファイルを一度実行します。このとき、関数がexportされていなければCloud Functionsに認識されないので、このような形になったのでしょう。
しかしながら見てのとおりですがNestFactory.create
やapp.init
は非同期関数(DIとかDB接続とかで時間かかるのかな?)なのでコールドスタート復帰後は最初のリクエストがNestの初期化前にExpressに渡されてしまうため、リクエストが処理できずに終わってしまいます。
これを回避するには、リクエストが来るたびに初期化が終わっているかを判断させる必要があります。結果、以下のような形になります。
const applicationReady = (async () => {
const app = await NestFactory.create(AppModule, new ExpressAdapter(server));
app.enableCors();
return app.init();
})();
export const api = functions
.region('asia-northeast1')
.https.onRequest(async (...args) => {
await applicationReady;
server(...args);
});
みんなPromiseが入った変数の命名規則どうしてるんだろう...。説明し辛い...。
Nest.jsみたいな大きいフレームワークをFaaSで動かすのどうかな?と思いましたがFaaSのメリットは大きいのでまあありかなと。Nestはそんなに重くないですしね。
Nest.js人気出てきてますがフレームワークが分厚くなりすぎるとRailsのような末路をたどってしまうので、少々怖いところではあります。