NestJS をFirebase Functions で動かすための環境構築について


こんにちは、おかきょーといいます。

Firebase では、Express を利用して Firebase Functions 上でサーバーサイドを構築することができます。Firebase Functions を利用してAPIやボット開発を行っていきたいと考えていた時、TypeScript 界隈で徐々に注目を集めている NestJS にも関心を持ちました。この記事では、Firebase 上で NestJS を動作するように設定したことについて書いていきます。

NestJS とは

NestJS は Node.js 上で動くバックエンドフレームワークです。

主に次のような特徴が挙げられます。

  • TypeScript に完全対応している
  • 依存関係を分離したクリーンアーキテクチャーなアプリが開発できる
  • TypeORM, GraphQL, Websocket, や Express のミドルウェアにも対応
  • Swagger を使ったドキュメントも自動生成してくれるように設定可能
  • CLI を利用してテンプレートが容易に作られる
  • Express をコアにして実装されているが、リクエスト処理を高速にするために Fastify に変更可能
  • マイクロサービスアーキテクチャに役立つライブラリやドキュメントも完備

そのため、このアプリは、大規模なアプリ開発をするうえで多くの役立つツールがを備えた今後期待されるフルスタックフレームワークです。

環境構築

まずは、Firebase Functions で、この NestJS フレームワークが動作するように環境を構築します。

今回の開発について、使用した開発環境は次の通りです。

OS: Windows Pro 
nodejs: 12.16.1
@nest/cli: 7.1.4
firebase-tools: 8.2.0

プロジェクトを立ち上げる

まず, NestJS の CLI をインストールしてプロジェクトを作成します。

もしCLIを 利用せずにアプリを開発するなら、

npm install --save @nestjs/core @nestjs/common rxjs reflect-metadata 

とすることで、既存のプロジェクトに直接インストールすることもできます。

この時、生成されたファイルは次のようになっています。

.
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   ├── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json

この記事では、CLI を使うことを前提にアプリを開発していきます。
今回のプロジェクト名を Firebase-app として進めていきます。

npm install -g @nestjs/cli
nest new Firebase-app

作業用ディレクトリに移動後、アプリのセキュリティを向上するために次のライブラリをインストールします。

npm install --save helmet 
npm install --save-dev @types/helmet 

このとき、/src/main.ts に先ほどのセキュリティ用のフレームワークを設定します。

/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as helmet from "helmet";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // ここにセキュリティについての設定を追加する
  app.use(helmet());

  // NestJS はこれでCORS の設定を追加することができる
  app.enableCors();
  await app.listen(3000);
}
bootstrap();

cd firebase-app
npm run start:dev 

として起動すると、http://localhost:3000 で、次のように
Hello World が表示されていれば成功です。

これでNestJS の初期設定は完了です。

Firebase の設定

次に Firebase の設定を行います。
まず、Firebase を設定するための CLI ライブラリを導入します。

npm install -g firebase-tools  

ここで、プロジェクトのディレクトリで、

firebase login 

として、Firebase が使えるように設定したのち、Firebase で新規にプロジェクトを立ち上げます。

立ち上げ方はこちらを参照にしてみてください。

次に、Firebase にこのプロジェクトを登録します。

firebase init functions 

として、Firebase にデプロイするための設定ファイルや functions ディレクトリが生成されていると思います。

けれども、

rm -rf functions 

などととして、この functions ディレクトリを削除して、package.json を次のように書き直します。

package.json

{
  // (省略)
  "main": "dist/index.js",
  "engines": {
    "node": "10"
  },
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    // (以下を追加)
    "deploy": "npm run build && firebase deploy --only functions",
    "logs": "firebase functions:log",
    "fb-serve": "npm run build && firebase emulators:start --only functions",
    "fb-shell": "npm run build && firebase functions:shell",
    "fb-start": "npm run fb-shell"
  },
  (省略)
}

ここで、/src ディレクトリに新たに index.ts を追加します。今回は、この index.ts ファイルが Firebase 上で実行します。

/src/index.ts
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import * as express from 'express';
import * as functions from 'firebase-functions';
import * as helmet from "helmet"

const server = express();

export const createNestServer = async (expressInstance) => {
  const app = await NestFactory.create(
    AppModule,
    new ExpressAdapter(expressInstance),
  );

  // ここにセキュリティについての設定を追加する
  app.use(helmet());
  app.enableCors();



  console.log("the server is starting @ firebase")
  return app.init();
};



createNestServer(server)
    .then(v => console.log('Nest Ready'))
    .catch(err => console.error('Nest broken', err));

export const api = functions.https.onRequest(server);

では次に、ローカル上で問題なく動作するかどうかを調べてみます。

npm run fb-serve

これを実行することで次のように

localhost:5001/(プロジェクト名)/(デプロイサーバー先)/api で Hello World! と表示されていれば成功です。

※(注意)
この記事では、このように /src/index.ts を追加して Firebase がこのコード上で動作するように設定しましたが、/src/main.ts を書き直して、この main.ts で動かせるようにすることもできます。

デプロイ

動作することが確認できたら、

npm run deploy 

として Firebase 上にデプロイします。これで Firebase 上でも動かせるようになったと思います。お疲れさまでした!

最後に

これで、Firebase 上で NestJS サーバーが動作するように設定できました。今後は、NestJS の詳細な文法の書き方や例外の実装法、外部 API と連携してボットといったアプリの開発、GitHub Actions を連携して CI/CD を設定方法について記事にしていきたいと考えています。

この記事を最後まで読んでいただき、ありがとうございました。

参考文献