Nxを使ってNext.jsとNestJSのmonorepo環境を作る

こちらの記事の内容が古くなっている箇所があったため、Nx v16.2.2でNext.jsとNestJSのモノレポ環境をキャッチアップしながら作ってみました。

https://qiita.com/UHNaKZ/items/ba95938ae1df97d4697a

## Nxのワークスペースを作成
```bash
npx create-nx-workspace@latest
```

作成完了
```
daichi@suyamadaichinoMacBook-Pro sample-2 % npx create-nx-workspace@latest

 >  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Choose what to create                 · integrated
✔ What to create in the new workspace   · next
✔ Repository name                       · sample
✔ Application name                      · front
✔ Default stylesheet format             · styled-components
✔ Enable distributed caching to make your CI faster · No

 >  NX   Creating your v16.2.2 workspace.

   To make sure the command works reliably in all environments, and that the preset is applied correctly,
   Nx will run "npm install" several times. Please wait.

✔ Installing dependencies with npm
✔ Successfully created the workspace: sample.
```

## NestJSを導入する
[公式ドキュメント](https://nx.dev/packages/nest#nxnest)
必要なプラグインを導入します。
```bash
yarn add -D @nx/nest
```

ワークスペース作成時に作ったフロントに統合
```bash
nx g @nx/nest:app api --frontendProject front
```

## ディレクトリ構造
ここまでで`apps/api`と`apps/front`ができているはずです。
```
.
├── .vscode
│   └── extensions.json
├── apps
│   ├── api
│   │   ├── src
│   │   │   ├── app
│   │   │   │   ├── app.controller.spec.ts
│   │   │   │   ├── app.controller.ts
│   │   │   │   ├── app.module.ts
│   │   │   │   ├── app.service.spec.ts
│   │   │   │   └── app.service.ts
│   │   │   ├── assets
│   │   │   │   └── .gitkeep
│   │   │   └── main.ts
│   │   ├── .eslintrc.json
│   │   ├── jest.config.ts
│   │   ├── project.json
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── webpack.config.js
│   ├── api-e2e
│   │   ├── src
│   │   │   ├── api
│   │   │   │   └── api.spec.ts
│   │   │   └── support
│   │   │       ├── global-setup.ts
│   │   │       ├── global-teardown.ts
│   │   │       └── test-setup.ts
│   │   ├── .eslintrc.json
│   │   ├── jest.config.ts
│   │   ├── project.json
│   │   ├── tsconfig.json
│   │   └── tsconfig.spec.json
│   ├── front
│   │   ├── app
│   │   │   ├── api
│   │   │   │   └── hello
│   │   │   │       └── route.ts
│   │   │   ├── global.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── public
│   │   │   ├── .gitkeep
│   │   │   └── favicon.ico
│   │   ├── .DS_Store
│   │   ├── .eslintrc.json
│   │   ├── index.d.ts
│   │   ├── jest.config.ts
│   │   ├── next-env.d.ts
│   │   ├── next.config.js
│   │   ├── project.json
│   │   ├── proxy.conf.json
│   │   ├── tsconfig.json
│   │   └── tsconfig.spec.json
│   ├── front-e2e
│   │   ├── src
│   │   │   ├── e2e
│   │   │   │   └── app.cy.ts
│   │   │   ├── fixtures
│   │   │   │   └── example.json
│   │   │   └── support
│   │   │       ├── app.po.ts
│   │   │       ├── commands.ts
│   │   │       └── e2e.ts
│   │   ├── .eslintrc.json
│   │   ├── cypress.config.ts
│   │   ├── project.json
│   │   └── tsconfig.json
│   ├── .DS_Store
│   └── .gitkeep
├── libs
│   └── .gitkeep
├── tools
│   └── tsconfig.tools.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .prettierignore
├── .prettierrc
├── jest.config.ts
├── jest.preset.js
├── nx.json
├── package-lock.json
├── package.json
├── README.md
├── tsconfig.base.json
└── yarn.lock
```

## proxyConfigが非推奨とのことなので、推奨の方法に対応
[公式ドキュメント](https://nx.dev/packages/next/executors/server#proxyconfig)

https://nextjs.org/docs/api-reference/next.config.js/rewrites

```javascript:next.config.js
const nextConfig = {
  nx: {
    // Set this to true if you would like to to use SVGR
    // See: https://github.com/gregberge/svgr
    svgr: false,
  },
 // 追記
  async rewrites() {
    return [
      {
        source: '/api',
        destination: 'http://localhost:3000/',
      },
    ];
  },
};

```

```diff:front/project.json
// 省略
    "serve": {
      "executor": "@nx/next:server",
      "defaultConfiguration": "development",
      "options": {
        "buildTarget": "front:build",
        "dev": true,
-       "proxyConfig": "apps/front/proxy.conf.json"
+       "port": 3001
      },
      "configurations": {
        "development": {
          "buildTarget": "front:build:development",
          "dev": true
        },
        "production": {
          "buildTarget": "front:build:production",
          "dev": false
        }
      }
    },
// 省略
```

## ワンコマンドでAPIサーバーとFrontのサーバーを立ち上げる
開発時は`yarn dev`でAPIもフロントも起動します。

`/`へのアクセスでフロント、`/api`でapiにproxyされます。

```json:package.json
// 省略
  "scripts": {
    "dev": "nx run-many -t serve front api"
  },
// 省略
```

## フロントはVercelにデプロイ
フロントをVercelにデプロイしましょう。
公式にドキュメントがあります。

https://nx.dev/recipes/other/deploy-nextjs-to-vercel

あ、そういえばこういう問題にハマりました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">まさにこの条件を引き当てて、3時間ぐらいハマってた…<a href="https://t.co/Pgd1LK9bic">https://t.co/Pgd1LK9bic</a></p>&mdash; どんちゃん (@d0nchaaan) <a href="https://twitter.com/d0nchaaan/status/1663604180576268288?ref_src=twsrc%5Etfw">May 30, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## APIはAppRunnerにデプロイ
CORS対策は事前にしておきましょう。

apprunner.yamlはこんな感じ(簡略化してます)
```yaml:apprunner.yaml
version: 1.0
runtime: nodejs16
build:
  commands:
    pre-build:
      - yarn install
    build:
      - npx nx build api --prod
run:
  runtime-version: 16.19.0
  command: npx nx run api:serve --configuration=production
  network:
    port: 3001
    env: APP_PORT
  env:
    - name: APP_PORT
      value: 3001
```

## 拡張機能
VSCodeの拡張機能、NX Consoleは入れておきましょう。コマンドを忘れてもどうにかなります。

https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console

他にも便利な機能の発見があるので、公式ドキュメントを眺めてみると楽しいです(僕もまだまだ知らない機能が)

https://nx.dev/getting-started/intro