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

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

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

## NestJS とは

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3237313730382f33373934666139342d363832332d663230362d336338622d3663633331313962.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271708/376aa123-84d1-fb7d-6791-bbceb3127e82.png)


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を 利用せずにアプリを開発するなら、

```bash
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 として進めていきます。

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

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

```bash
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();

```


```bash
cd firebase-app
npm run start:dev 
```
として起動すると、http://localhost:3000 で、次のように
Hello World が表示されていれば成功です。

![スクリーンショット (75).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271708/861e4180-d7e5-a9df-df63-d4311a6093b1.png)



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

### Firebase の設定

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

```bash
npm install -g firebase-tools  
```
ここで、プロジェクトのディレクトリで、

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

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

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

```bash 
firebase init functions 
```

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

けれども、

```bash
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);
```

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

```bash
npm run fb-serve
```

これを実行することで次のように
![スクリーンショット (76).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271708/d9a13f98-294e-295b-bd30-848cdfa9d8f3.png)

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

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

### デプロイ

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

```bash
npm run deploy 
```

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

## 最後に

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

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

## 参考文献

- NestJS 公式サイト: https://docs.nestjs.com/
- https://fireship.io/snippets/setup-nestjs-on-cloud-functions/
- https://qiita.com/elipmoc101/items/9b1e6b3efa62f3c2a166