はじめに
どうもyoshiiです。
今まで、いろんなwebアプリ作ってきたんですが、どれもフロントエンドとバックエンドを別サーバーに置いてSPAで実装するばかりで、たまにはSPA以外もやってみたいなと思ったのが発端でした。
ちなみに今まで作ったアプリとかは大体こちらにまとめてます。基本的になんのありがたみもないようなものを作っています。
暇人は見てね。
やることはタイトルの通りです。
1つのプロジェクト内でNext.jsとNestJSを配置し、
フロントエンドとバックエンドをそれぞれ担当させ、1つのサーバーに配置し、SSR(サーバーサイドレンダリング)まで実装します。
正直、今までwebアプリ作ったことないような人でも、愚直にこの記事通りやればwebアプリの雛形が作れると思いますので、そういう人も是非是非読んでみてください。
質問も答えられる範囲であれば、答えます!
イメージとしては、yarn start
でNestJSサーバーが立ち上がり、サーバーにアクセスしたらNext.jsで実装したフロントエンドがレンダリングされるって感じです。
ちなみに、Next.js公式はSSRより圧倒的にSSGを推奨していますが、サーバーで複雑な動きさせたいってなるとNestJSのプロジェクトにNextを配置するような形が良いとも思います。
なので、とにかく色々作りたいタイプには、まずは汎用性の高いSSRからやるのをおすすめします。
SSRはパフォーマンスがSSGよりも落ちる代わりに、SSRさえできればどんな形のアプリにでもオールマイティに対応できると思います。
またSSRはSEO面も良い(検索した時に上の方に出てきやすい)です。
それに、このNext.jsでSSRを実装したらちょっとビルド方法変えて関数名変えたらSSGもできるので、SSGしたい人も参考になるかもしれません。
目次
前提知識
Next.jsとかNestJSとかSSRとか、そもそもなんだそれはって人向けに筆者の曖昧な知識で解説します。
- React: webアプリの見えてる部分(フロントエンド)の開発をスマートにできる部品の集まり。(ライブラリ)
- Next.js: Reactをさらにスマートにして、できることも増やしたやつ。React用フレームワーク。
- NestJS: webアプリの見えない部分(バックエンド)の開発をスマートにできる雛形。(フレームワーク)
- SSR: 各ページにアクセス(遷移)するたびにサーバー側にリクエストが飛ぶような方式。よくわかんない人はスルーしていいです。
- TypeScript: 言語。JavaScriptを静的型付け言語に拡張したもの。安全に開発しやすい。
- heroku: 無料枠が優秀なPaaS。NestJSを簡単に動かせるサーバー。ここにデプロイ(公開)するので、アカウントを作っておいてください。
- モノレポ: フロントエンドとバックエンドを1個のリポジトリにまとめて、1つのコマンドで起動するようなプロジェクト(だと思っています)。
- yarn: nodejsのパッケージマネージャー。jsで作られたライブラリ(機能の部品)を管理するやつ。
- npmでもいいですが、個人的にyarnが好きなのでよくわかんない人もとりあえず入れてください。
- Homebrewってやつでnodenvとかanyenvとかを入れて、npmからyarnを入れるのが普通かな…?
- SSRとかSPAとかの参考記事: https://shimablogs.com/spa-ssr-ssg-difference
- node.jsのインストール参考記事: https://qiita.com/kyosuke5_20/items/eece817eb283fc9d214f
こんなもんでしょうか。わかんない言葉あったら質問ください。多分俺もわかんないですが、一緒に考えましょう。質問がくるたびにここが増えていくような気がします。
構成とか
PC: macOS Big Sur
nodeのversion: v14.17.3
パッケージマネージャー: yarn
バックエンド: NestJS
フロントエンド: Next.js
言語: TypeScript
サーバー: Heroku
GitHub
手順とかどうでもいいからテンプレ見せろって感じなら、俺の公開してるリポジトリ見てください。
参考にしたもの
正直、実装に関しては以下の記事ほぼそのままです。
deepLさんで和訳しながら実装しました。
実装
では実装に入っていきます!
プロジェクトの作成
まずはプロジェクトを作ります。
nest cliを入れておきましょう。
NestJS公式を見て入れようね。
ターミナルで以下を実行
nest new nest-next-template
この時のnest-next-template
ってとこは好きなプロジェクトの名前にしてね。
npmにするかyarnにするか聞かれるので好きな方選んでね。(筆者はyarn派です)
今回、テストはちょっと考えないことにしたいので、nest cliによって勝手に作られるsrc/test
ディレクトリと、src/app.controller.spec.ts
は削除しておきましょう。
そして、NestJSプロジェクトが作成されたら、src
ディレクトリの中にpages
, server
, shared
ディレクトリを作りましょう。
現状src
ディレクトリの中身はこんな感じ
src/
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── main.ts
├── pages/
├── server/
└── shared/
〜 各ディレクトリの解説 〜
- pages/: Next.jsのpagesに相当するファイル群をここに置いていく
- server/: NestJSのファイル群をここに置いていく
- shared/: Next.jsとNestJSで共通の情報をここに配置する。型定義ファイルとかが多い。
なので、まずはsrc/server
ディレクトリにnest cliで作成されたファイルたちを移動しましょう。
移動した後はこんな感じ
src/
├── pages
├── server
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
└── shared
次に、nest-cli.json
ファイルを以下のように編集。
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"entryFile": "main"
}
これでプロジェクトの初期化はとりあえず終わり。
各種ライブラリの導入
Next.js関連のライブラリを入れます。
ターミナルで以下を入力してください。
yarn add next react react-dom
yarn add -D @types/react @types/react-dom eslint-config-next
上記に加えて先で必要になるライブラリも今のうちに入れておきましょう
ターミナルで以下を入力。
yarn add nest-next axios
現状必要なライブラリはこんなものですかね。
Reactはフレームワークではなくライブラリなので、こんな感じで、最小構成のフロントエンド開発環境をNestJSプロジェクトに簡単に追加できたりします。(どこかで読んだ話ですが…)
ここから本格的に実装に入っていきましょう。
Next.jsの実装
フロントエンドの実装に入る前に、tsconfigファイルを用意してあげましょう。
ルートディレクトリにtsconfig.server.json
を作成します。
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false
},
"include": [
"./src/server/**/*.ts",
"./src/shared/**/*.ts",
"./@types/**/*.d.ts"
]
}
また、ルートディレクトリに存在しているtsconfig.json
を、以下のように編集します。
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
その後、package.json
のscriptsの部分を以下のように書き換えます。これでyarn build
でNext.jsとNestJS両方のビルドが行われたり、とにかく上手いことやってくれるようになります。
"scripts": {
"prebuild": "rimraf dist",
"build": "yarn build:next && yarn build:nest",
"build:next": "next build",
"build:nest": "nest build --path ./tsconfig.server.json",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "node dist/server/main.js",
"start:prod": "node dist/server/main.js",
"start:next": "next dev",
"start:dev": "nest start --path ./tsconfig.server.json --watch",
"lint": "eslint \"{src,apps,libs,test}/**/*.tsx?\" --fix"
},
よくわからん人は、とりあえずpackage.json
に以下の内容をコピペしたってください。
{
"name": "nest-next-template",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "yarn build:next && yarn build:nest",
"build:next": "next build",
"build:nest": "nest build --path ./tsconfig.server.json",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "node dist/server/main.js",
"start:prod": "node dist/server/main.js",
"start:next": "next dev",
"start:dev": "nest start --path ./tsconfig.server.json --watch",
"lint": "eslint \"{src,apps,libs,test}/**/*.tsx?\" --fix"
},
"dependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"axios": "^0.26.0",
"nest-next": "^9.6.0",
"next": "^12.0.10",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@types/express": "^4.17.13",
"@types/jest": "^27.0.1",
"@types/node": "^16.0.0",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"eslint": "^7.30.0",
"eslint-config-next": "^12.0.10",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"supertest": "^6.1.3",
"ts-jest": "^27.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "^3.10.1",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
package.json
を編集した後は、一応ターミナルでyarn install
しておきましょうか。
豆知識ですが、yarn install
はyarn
と入力するだけで実行されます。どうでもいいですね。
次に、src/pages/
以下に_app.tsx
とindex.tsx
、2つのファイルを追加
import type { AppProps } from 'next/app';
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;
import type { NextPage } from 'next';
const Home: NextPage = () => {
return <h1>Home</h1>;
};
export default Home;
この時点で、ターミナルにyarn start:next
と入力して http://localhost:3000/ にアクセスしてやると、Homeって出てくると思います。
あと、.gitignore
にNext.jsのビルドディレクトリを追加しましょう。
# compiled output
/dist
/node_modules
/.next
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
サーバーサイドの編集
ここからサーバーサイド(NestJS)のファイルを編集していきます。
まずは、main.ts
。デプロイした時にポート番号を入力しなくて良くなるようにします。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT || 3000);
}
bootstrap();
次に、app.module.ts
を編集。nest-next
ライブラリを使って、Next.jsをレンダリングできるようにします。dev: true
を指定すると、ファイルの更新を監視して変更してくれます。(ホットリロード的な?)
import { Module } from '@nestjs/common';
import { RenderModule } from 'nest-next';
import Next from 'next';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [RenderModule.forRootAsync(Next({ dev: true }), { viewsDir: null })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
最後に、app.controller.ts
を編集。@Render
デコレータを使って、index.tsx
をレンダリングします。
import { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
@Render('index')
home() {
return {};
}
}
ここまでできたら、一度yarn start:dev
とターミナルに入力してみてください。
ルートディレクトリにNext.jsとNestJSのビルドディレクトリがそれぞれ生成され、NestJSのサーバーが起動します。
http://localhost:3000/ にアクセスすると、index.tsx
がレンダリングされ、Homeって表示されると思います。
とりあえずここまででcommitを載せておきます。
サーバーサイドからデータを受け取って表示してみる
まず、型定義ファイルsrc/shared/types/index.d.ts
を作成し、Story型を作ります。
export type Story = {
id: number;
title: string;
description: string;
};
とりあえず、データベース用意するの面倒なので、データはapp.service.ts
内に定数として直接持たせちゃいましょう。
本来はデータベースからfetchしてくると思ってください。
import { Injectable } from '@nestjs/common';
import { Story } from 'src/shared/types';
const stories: Story[] = [
{
id: 1,
title: '国歌',
description: '首相が豚と…',
},
{
id: 2,
title: '1500万メリット',
description: '超管理社会',
},
{
id: 3,
title: '人生の軌跡のすべて',
description: '記憶をデータとして正確に閲覧できる世界の話',
},
];
@Injectable()
export class AppService {
getStories() {
return stories;
}
}
最近ネトフリで見て面白かった「ブラック・ミラー」というドラマシリーズのタイトルとあらすじを3つ用意してみました。(関係ないですがとてもおすすめのドラマです)
次に、app.controller.ts
でサービスを呼び出して、Storyの配列を返すAPIを定義しましょう。
import { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
@Render('index')
home() {
return {};
}
@Get('/api/stories')
public getStories() {
return this.appService.getStories();
}
}
では、Story配列をフロントエンドで受け取って表示してみましょう。
まず、axios
を使ってフロントエンドとバックエンドの通信を行うのですが、ベースURLを省略したいのと、application/json
形式で通信したいので、それをラップしてexportするファイルを用意します。
src/shared/lib/apiClient.ts
ファイルを以下のように作成します。
import axios from 'axios';
export const apiClient = axios.create({
baseURL: 'http://localhost:3000/',
responseType: 'json',
headers: {
'Content-Type': 'application/json',
},
});
そして、src/pages/index.tsx
にgetServerSideProps
関数を用意して、サーバーサイドでAPIを叩きましょう。
import type { GetServerSideProps, NextPage } from 'next';
import { apiClient } from 'src/shared/lib/apiClient';
import { Story } from 'src/shared/types';
type HomeProps = {
stories: Story[];
};
export const getServerSideProps: GetServerSideProps<HomeProps> = async () => {
const response = await apiClient.get<Story[]>('/api/stories');
return { props: { stories: response.data } };
};
const Home: NextPage<HomeProps> = (props) => {
const { stories } = props;
return (
<div>
<h1>Home</h1>
<ul>
{stories.map((story) => (
<li key={story.id}>
{story.id}: {story.title}
</li>
))}
</ul>
</div>
);
};
export default Home;
さらに、src/server
ディレクトリ以外のディレクトリのファイルもNestJSのビルドファイルに含まれることになるので、nest-cli.json
を以下のように編集しましょう。
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"entryFile": "server/main"
}
これで、yarn start:dev
を実行して、 http://localhost:3000/ にアクセスすると、こんな感じの画面が見えるはず
これらの情報はすべてサーバーサイドで取得しているので、動的ogpなんかにも使えます。
もし、うまく行かなかった人は、一回yarn build
してから、再度yarn start:dev
を実行してみてください。
NestJSのビルドディレクトリdist
の中を見て、main.jsがserverディレクトリ内にあるか確認してください。
ここまででcommitしておきます。
動的ルーティングを実装
まずはapp.service.ts
に、idを受け取ってstoryを返却する関数を定義します。
import { Injectable } from '@nestjs/common';
import { Story } from 'src/shared/types';
const stories: Story[] = [
{
id: 1,
title: '国歌',
description: '首相が豚と…',
},
{
id: 2,
title: '1500万メリット',
description: '超管理社会',
},
{
id: 3,
title: '人生の軌跡のすべて',
description: '記憶をデータとして正確に閲覧できる世界の話',
},
];
@Injectable()
export class AppService {
getStories() {
return stories;
}
getStory(id: number) {
return stories.find((story) => story.id === id) ?? null;
}
}
次に、インターセプターというやつを実装します。rxjs
使ってややこしい感じですが、簡単に言うとこれでNestJSのcontrollerからNext.jsのgetServerSideProps
にパスパラメーターを渡してるってことだと思います。
インターセプターのドキュメント(日本語訳): https://zenn.dev/kisihara_c/books/nest-officialdoc-jp/viewer/overview-interceptors
src/server/params.interceptor.ts
を以下のように作成します。
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Request } from 'express';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class ParamsInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const request = context.switchToHttp().getRequest() as Request;
return next.handle().pipe(
map((data) => {
return {
...request.query,
...request.params,
...data,
};
}),
);
}
}
そして、app.controller.ts
のNext.jsをレンダリングする関数に、UseInterceptors
デコレーターをつけてあげます。
import {
Controller,
Get,
Param,
Render,
UseInterceptors,
} from '@nestjs/common';
import { AppService } from './app.service';
import { ParamsInterceptor } from './params.interceptor';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
@Render('index')
@UseInterceptors(ParamsInterceptor)
home() {
return {};
}
@Get(':id')
@Render('[id]')
@UseInterceptors(ParamsInterceptor)
public storyPage() {
return {};
}
@Get('/api/stories')
public getStories() {
return this.appService.getStories();
}
@Get('/api/story/:id')
public getStory(@Param('id') id: number) {
return this.appService.getStory(Number(id));
}
}
storyPage()
関数で、 http://localhost:3000/:id にアクセスされた時にsrc/pages/[id]/index.tsx
をレンダリングします。
getStory
関数で、パスパラメータからidを受け取ってストーリーを返します。
仕上げにフロントエンド側を作成しましょう。
src/pages/[id]/index.tsx
を以下のように作成します。
import { GetServerSideProps, GetServerSidePropsContext, NextPage } from 'next';
import Link from 'next/link';
import { apiClient } from 'src/shared/lib/apiClient';
import { Story } from 'src/shared/types';
type StoryPageProps = {
story: Story;
};
export const getServerSideProps: GetServerSideProps<StoryPageProps> = async (
ctx: GetServerSidePropsContext,
) => {
const id = ctx.query.id;
if (id) {
const response = await apiClient.get<Story | null>(`/api/story/${id}`);
return response.data
? {
props: { story: response.data },
}
: { notFound: true };
} else {
return {
notFound: true,
};
}
};
const StoryPage: NextPage<StoryPageProps> = (props) => {
const { story } = props;
return (
<div>
<Link href={'/'}>
<a>Home</a>
</Link>
<h1>story: {story.title}</h1>
<p>あらすじ: {story.description}</p>
</div>
);
};
export default StoryPage;
最後に、src/pages/index.tsx
からのリンクも用意しましょう。
import type { GetServerSideProps, NextPage } from 'next';
import Link from 'next/link';
import { apiClient } from 'src/shared/lib/apiClient';
import { Story } from 'src/shared/types';
type HomeProps = {
stories: Story[];
};
export const getServerSideProps: GetServerSideProps<HomeProps> = async () => {
const response = await apiClient.get<Story[]>('/api/stories');
return { props: { stories: response.data } };
};
const Home: NextPage<HomeProps> = (props) => {
const { stories } = props;
return (
<div>
<h1>Home</h1>
<ul>
{stories.map((story) => (
<li key={story.id}>
<Link href={`/${story.id}`}>
<a>
{story.id}: {story.title}
</a>
</Link>
</li>
))}
</ul>
</div>
);
};
export default Home;
これで、yarn start:dev
して、http://localhost:3000/ にアクセスすると、リンクが表示されます。(ダメだったら先にyarn build
してね)
また、リンクをクリックしたり、http://localhost:3000/1 にアクセスすると、APIを叩いて以下のようにタイトルとあらすじが表示されます。
これで実装完了です。
あとはSSRでサーバーから好きにデータ受け取っちゃいましょう!
ここまでのcommitはこちら
デプロイ
では、いよいよデプロイしていきましょう
herokuのアカウントを作ってログインしておいてください。
メアドがあれば無料でアカウント作成できるし、クレカ情報を登録すれば無料でLinux コンテナを990時間とか起動できるので、完全無料で1個のサービスを常駐させれます。
ログインしたら、create new appを押すか、以下にアクセスしてください。
App nameに好きな名前を入力してください。筆者はnest-next-template-in-heroku
にしました。
入力できたら、Create appボタンを押してください。
これでデプロイ先のLinux コンテナが起動しました。
では、ここにGitHub Actionsでデプロイします。
.github/workflows/deploy.yml
を以下のように作成します。
name: Deploy
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{secrets.HEROKU_API_KEY}}
heroku_app_name: ${{secrets.HEROKU_APP}}
heroku_email: ${{secrets.HEROKU_EMAIL}}
これで、masterブランチにpushするとデプロイが走りますが、pushするのはちょっと待ってくださいね。
pushする先のGitHubリポジトリの設定を変更します。(まだ作成していない人は作成しておいてください)
Settings -> Secrets -> Actions から 「New repository secret」ボタンを押してください。
NameにHEROKU_API_KEY
、ValueにherokuのAPIキーを入力して「Add secret」ボタンを押して保存してください。
herokuのAPIキーはAccount Settingsから、API Key
のところの「Reveal」ボタンを押して表示されたAPIキーをコピペしてあげてください。
同様に、「New repository secret」ボタンから、HEROKU_APP
, HEROKU_EMAIL
を設定しましょう。
- HEROKU_APP: herokuのapp name(筆者の場合は
nest-next-template-in-heroku
) - HEROKU_EMAIL: herokuに登録したメールアドレス
次に、baseURLを環境によって切り替えます。
herokuでApp name一覧からApp nameを選択してSettingsからConfig Varsに、KEY: NODE_ENV, VALUE: productionを設定して、Addボタンを押してください。
これで、デプロイされた時に環境変数process.env.NODE_ENV
で参照することができます。
これに伴い、shared/lib/apiClient.ts
を以下のように編集します。
DEPLOY_URL ってところは、デプロイ先のURLを入力してください。本来は.env
とかで定義すべきですが、テンプレートでやるの面倒なので今回はやりません。
ちなみに公開してるリポジトリでは最終的に環境変数も導入してるので、よかったら見てね。
import axios from 'axios';
const nodeEnv = process.env.NODE_ENV ?? 'development';
export const apiClient = axios.create({
baseURL:
nodeEnv === 'production'
? DEPLOY_URL
: 'http://localhost:3000/',
responseType: 'json',
headers: {
'Content-Type': 'application/json',
},
});
また、src/server/app.module.ts
のdevオプションを変更します。
import { Module } from '@nestjs/common';
import { RenderModule } from 'nest-next';
import Next from 'next';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
RenderModule.forRootAsync(Next({ dev: false }), { viewsDir: null }),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
これによりNext.jsのdevモードが解除されます。
このオプションに関しても、本来なら環境変数を参照して、開発環境とでデプロイ先で切り替えるような実装が望ましいと思います。
最後にルート直下にProcfile
を追加しましょう。これで起動コマンドを指定できます。
ビルドはデプロイ後に自動で走るので、起動コマンドのyarn start:prod
を指定してあげます。
web: yarn start:prod
これらを設定できたら、コミットしてmasterブランチにプッシュしましょう。
すると、GitHub Actionsが走って…
デプロイ先のURL(筆者の場合は https://nest-next-template-in-heroku.herokuapp.com/ )にアクセスすると、実装したHome画面が表示されると思います。
デプロイのcommitはこちら
まとめ
お疲れ様でした!
めちゃくちゃ丁寧にNext.js+NestJSのモノレポを作成する手順を解説してみました。
これ読んでwebアプリ作ったことないような人もトライしてくれるとすごい嬉しいです!
あと、Twitterもやってますが、特にありがたみのないツイートしかしてないです…
お酒が好きなので、酒飲みは良かったらフォローして友達になってください