Nx NestJSからNext.js SSG(静的サイト生成)を配信する方法

モノレポツールNxを使って、Next.jsのSSG(静的サイト生成)で作成した静的サイトをNext.jsアプリケーションから配信する方法を解説します。この方法により、フロントエンドとバックエンドを1つのプロジェクトにまとめることができます。

技術公開日 読了目安 2 分

はじめに

この記事では、モノレポツールNxを使って、Next.jsのSSG(静的サイト生成)で作成した静的サイトを、Next.jsアプリケーションから配信する方法を説明します。この方法を使うと、フロントエンドとバックエンドを1つのプロジェクトにまとめることができます。

1. フロントエンドとバックエンドのプロジェクトを作成する

Nxワークスペースとnestjsプロジェクトを作成する

任意のディレクトリに移動し、以下のコマンドを実行します。

npx create-nx-workspace@latest

✔ Where would you like to create your workspace? · sample-workspace
✔ Which stack do you want to use? · node
✔ What framework should be used? · nest
✔ Integrated monorepo, or standalone project? · integrated
✔ Application name · api
✔ Would you like to generate a Dockerfile? [https://docs.docker.com/] · No
✔ Enable distributed caching to make your CI faster · No

これにより、Nestjsのapiプロジェクトが作成されます。

Nextjsプロジェクトを作成する

nx g @nx/next:app web

✔ Which stylesheet format would you like to use? · css
✔ Which E2E test runner would you like to use? · cypress
✔ Would you like to use the App Router (recommended)? (Y/n) · true

上記のコマンドで作成されるディレクトリ構成は以下のようになります。

sample-workspace/
  |- apps/
  |    |- api/
  |    |- web/

2. Nextjs(フロントエンド)でSSG(静的サイト生成)を有効にする

静的ファイルへの対応を有効にするため、以下の変更を行います。

  • imagesにunoptimized: trueを追加する(SSGはnext/imageをサポートしていません)。
  • output: 'export'を追加する。
  • trailingSlash: trueを追加する。
const nextConfig = {
  nx: {
    // Set this to true if you would like to use SVGR
    // See: https://github.com/gregberge/svgr
    svgr: false
  }, 
  images: {
    unoptimized: true,
  }, 
  output: 'export', 
  trailingSlash: true
}; 

trailingSlash: trueを設定しない場合、Nestjs経由でNextjsにアクセスした際、ホームページ以外のページでブラウザをリロードするとホームページにリダイレクトされてしまいます。

NEXT.JS trailingSlash

3. Nestjs(バックエンド)にServeStaticModuleを追加する

serve-staticパッケージをインポートする

npm i @nestjs/serve-static

ServeStaticModuleを実装する

import { ServeStaticModule } from '@nestjs/serve-static'; 
import path, { join } from 'path'; 
// Other standard imports are omitted

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'web/.next'),
      exclude: ['/api/*']
    }),
  ], 
  controllers: [AppController], 
  providers: [AppService]
})
export class AppModule {}

上記のコードにより、エンドポイントが/apiで始まらない場合は、Nextjsの静的サイトである.nextディレクトリにリダイレクトされます。

この時点では.nextフォルダはまだ生成されていません。後述するNextjsのビルドを実行した際に生成されます。

4. package.jsonにビルド・起動コマンドを追加する

package.jsonファイルにビルドコマンドと起動コマンドを追加します。

"scripts": {
    "cd:build-api": "npx nx build api --prod",
    "cd:build-web": "npx nx build web --prod",
    "cd:build": "npm run cd:build-api && npm run cd:build-web",
    "cd:web-start:server": "cd dist/apps/web && npm run start",
    "cd:web-start:static": "npx serve dist/apps/web/.next",
    "cd:api-start": "node dist/apps/api/main.js"
}

次に、ビルドコマンドを実行します。

npm run cd:build

これにより、フロントエンド(web)とバックエンド(api)の両方のビルドファイルが生成されます。

最後に、起動コマンドを実行します。

npm run cd:api-start

これにより、バックエンドサーバーがlocalhostで起動し、ブラウザでNext.jsのページにアクセスできるようになります。

これらをデプロイすることで、フロントエンドとバックエンドを1つのサーバーに統合して配信できます。

参考として、ビルド後のディレクトリ構成は以下の通りです。

sample-workspace/
  |- apps/
  |    |- api/
  |    |- web/
  |
  |-dist/  <= generated after the build
  |   |- apps/
  |        |- api/   
  |        |- web/
  |             |- .next/
  |
  |- package.json