NestJSで始めるWebアプリ開発 ─ ブログサイトを作りながら学ぶプロジェクト構成と設定管理

  • nestjs
    nestjs
2025/04/11に公開

はじめに

この連載では、NestJSを使ってシンプルなブログアプリを開発しながら、Webアプリケーション開発の基礎から運用までを段階的に学んでいきます。
最終的には、投稿された記事をReactで表示できるシンプルなUIを備えたブログアプリを完成させ、Dockerによるローカル開発環境や、NeonなどのマネージドDBを用いた本番環境へのデプロイまで扱います。

NestJSは、TypeScriptベースで構築されたNode.jsのフレームワークで、Angularのようなモジュール構造を持ち、拡張性や保守性に優れた設計が可能です。
この特性を活かしながら、実際に動くアプリケーションを一歩ずつ作り上げていきましょう。

この連載で作るもの

  • NestJSを用いたブログ投稿API
  • Prismaを使った型安全なDB操作
  • .envを使い分けた環境ごとの設定管理
  • ログ・エラーハンドリング・テストといった運用を意識した構成
  • 最後にReactでブログ記事を表示し、Docker構成を用いて本番環境へデプロイ

この連載の全体像と今回の位置づけ

連載構成(予定)

  1. NestJSで始めるWebアプリ開発 ─ ブログサイトを作りながら学ぶプロジェクト構成と設定管理 ← 今回の記事
  2. NestJSで記事投稿APIを作ろう ─ Prisma導入とCRUD実装の基本
  3. アプリの信頼性を高める ─ ロギング・例外処理・テスト戦略
  4. PrismaとDB設計を深掘る ─ モデル・リレーション・運用設計
  5. ReactでUIを構築して本番へデプロイ ─ Dockerと環境構築

この記事(第1回)では、NestJSプロジェクトのセットアップと、今後の開発を支える「設定管理の基盤作り」をテーマに進めていきます。
シンプルな「Hello World」だけでなく、今後の構成を見据えた.env管理やディレクトリ設計まで丁寧に扱います。

対象読者と学べること

この連載は、次のような方を想定しています

  • NestJSに興味があり、実用的なアプリを通して学びたい方
  • PrismaやDockerなど、モダンな開発ツールを一緒に使いたい方
  • Webアプリを「とりあえず動かす」ではなく、設計や運用まで含めて学びたい方
  • Reactやフロントは後回しで、まずAPIやバックエンドの基盤をしっかり作りたい方

NestJSの基本から始めて、実際に使える構成で動くアプリケーションを完成させることを目指します。

プロジェクトの全体像と使用技術

この連載で構築するアプリケーションは、NestJSを中心に、データベースやフロントエンド、そして本番運用まで見据えた構成を取り入れたブログシステムです。
「シンプルな記事投稿・表示」を題材にしながらも、現場でも使える設計・運用ノウハウが自然に身につく構成を目指しています。

NestJSとは?

NestJSは、TypeScriptベースで構築されたNode.js向けのWebアプリケーションフレームワークです。
Angularにインスパイアされたモジュール構造とDI(依存性注入)コンテナを備え、シンプルなAPIからスケーラブルなマイクロサービスまで対応できます。

NestJSの主なメリットとしては以下が挙げられます。

  • アーキテクチャが明快で拡張しやすい
  • TypeScriptが前提なので、型安全な開発ができる
  • PrismaやGraphQL、Swaggerなどモダンな周辺技術との親和性が高い
  • ロギング、例外処理、設定管理、テストなどが最初から整理しやすい構成

使用技術スタック一覧

項目 技術 用途
フレームワーク NestJS API設計・バックエンドの基盤
ORM Prisma DBスキーマ管理と型安全な操作
データベース PostgreSQL(Docker) ローカル開発用DB
DBホスティング Neon / PlanetScale 本番環境用マネージドDB
設定管理 @nestjs/config .env を通じた環境ごとの設定管理
フロント React(+ Vite) ブログ記事を表示するUI部分
テスト Jest + Supertest 単体テスト・E2Eテスト
デプロイ Docker / Render 開発・本番環境の運用と公開

開発から本番までの構成イメージ

NestJSアプリはローカルのホスト環境で直接起動し、Dockerコンテナ化されたPostgreSQLと接続して開発を行います。
最終的には、Reactで構築したブログUIをNestJS経由で配信し、DockerコンテナとしてRenderなどにデプロイします。DBも外部サービス(Neon)を使い拡張性を考慮した構成にします。

ローカル環境

Image from Gyazo

本番環境

Image from Gyazo

NestJSのプロジェクトを作成しよう

ここから、実際にNestJSのプロジェクトを立ち上げていきます。
NestJSにはNest CLIという公式ツールが用意されており、数コマンドで初期構成が整ったプロジェクトを作ることができます。
このCLIを使えば、ディレクトリ構成やTypeScriptの設定なども自動でセットアップしてくれるため、スムーズに始められるのが魅力です。

Nest CLIのインストール

まずは、Nest CLIをグローバルにインストールします。
※すでにインストール済みの方は、nest --versionでバージョン確認してスキップしてOKです。

npm install -g @nestjs/cli

-g は「グローバルインストール」の意味です。これにより、どのプロジェクトからでも nest コマンドが使えるようになります。

プロジェクトを作成する

次に、CLIを使ってプロジェクトを新規作成します。
今回は nestjs-blog という名前でプロジェクトを作ってみましょう。

nest new nestjs-blog

すると、次のような対話形式の質問が表示されます。

Which package manager would you ❤️  to use?
❯ npm
  yarn
  pnpm
  • いつも使っているもの(例:npm)を矢印キーで選んでEnterを押します。
  • すると、自動的に必要な依存パッケージがインストールされます。

Image from Gyazo

プロジェクトの中に入ってみる

作成されたディレクトリに移動します。

cd nestjs-blog

中には、すでにNestJSの基本構成が整っています。

nestjs-blog/
├── src/
│   ├── app.controller.ts     ← ルーティング(URLの処理)
│   ├── app.module.ts         ← アプリ全体の構成を定義
│   ├── app.service.ts        ← ビジネスロジックを書く場所
│   └── main.ts               ← エントリーポイント(アプリの起動処理)
├── test/                     ← テストコード
├── package.json              ← 依存パッケージの一覧
└── tsconfig.json             ← TypeScriptの設定

アプリを起動してみる

次に、NestJSアプリを起動して動作確認をしてみましょう。
開発用のホットリロード付きで立ち上げます。

npm run start:dev

成功すれば、次のようなメッセージが表示されるはずです。

Image from Gyazo

この状態で、ブラウザで http://localhost:3000 にアクセスしてみてください。
「Hello World!」という文字列が表示されていれば、無事にNestJSアプリが起動しています!🎉

ここまでのまとめ

  • Nest CLIを使えば、複雑な設定不要でNestJSアプリがすぐ立ち上がる
  • TypeScriptやディレクトリ構成もすでに整っている
  • ブラウザで「Hello World!」が出れば、準備完了!

次のセクションでは、作ったプロジェクトを今後の開発に耐えられるような設計に整えていくために、環境変数や設定管理について掘り下げていきます。

ディレクトリ構成の基本とモジュール設計

NestJSのプロジェクトは、明確な責務分離と拡張性を意識したディレクトリ構成が特徴です。
はじめて見ると「ファイルが多いな」と感じるかもしれませんが、それぞれに意味があり、理解して使いこなせば非常にスッキリとした開発体験になります。

この章では、最初に作成された構成を紐解きながら、NestJSの設計思想に触れていきましょう。

NestJSの標準的な構成を理解する

Nest CLIでプロジェクトを作成すると、src/ ディレクトリ以下は次のような構成になっています。

src/
├── app.controller.ts    // ルーティングの定義
├── app.module.ts        // アプリ全体の構成(モジュールの登録)
├── app.service.ts       // ビジネスロジック(処理の本体)
└── main.ts              // エントリーポイント(アプリの起動)

これらの役割は以下の通りです。

ファイル 役割
main.ts アプリケーションを起動する(NestFactory.create()など)
app.module.ts アプリのモジュールをまとめる中心。各機能を登録するハブ
app.controller.ts HTTPリクエスト(ルーティング)を受け取る窓口
app.service.ts 実際の処理・ビジネスロジックを担う場所

AppModuleとは何か?

NestJSでは、すべての機能は「モジュール」という単位で構成されます。
AppModule はその中でも「ルートモジュール(= アプリ全体の入口)」として位置づけられており、以下のような役割を果たします。

src/app.module.ts
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
  • imports: 他のモジュールをここに読み込む(例:DatabaseModuleやPostsModuleなど)
  • controllers: このモジュールが持つコントローラー(ルーティング担当)
  • providers: サービスやユーティリティなど、DI(依存性注入)されるクラスたち

すべてのモジュールは最終的にAppModuleに登録されることでNestJSに認識されます。

Controller / Service の責務

NestJSは、ControllerとServiceを明確に分けることで、責任の分離(Separation of Concerns)を徹底しています。

役割 担当 実装ファイル例
Controller ルーティングと入力の受付 app.controller.ts
Service ビジネスロジック・処理の実行 app.service.ts
src/app.controller.ts
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}
src/app.service.ts
@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

Controllerは「ルーティングやパラメータの処理」までに留め、処理の本体は必ずServiceに委ねる。これにより、以下のようなメリットが得られます。

  • 責任が明確になり、テストや保守がしやすい
  • ロジックをControllerに書かないので、コードが読みやすくなる
  • Serviceを再利用できる(複数のControllerやイベント処理でも)

モジュール単位の構成が基本

NestJSでは、機能ごとに「○○Module」単位で分けるのが推奨されます。

src/
├── posts/
│   ├── posts.module.ts
│   ├── posts.controller.ts
│   └── posts.service.ts
├── users/
│   ├── users.module.ts
│   └── ...

このように整理することで、規模が大きくなっても見通しの良いコードベースが維持できます。

ここまでのまとめ

  • NestJSは「モジュール」を中心に構成されるアーキテクチャ
  • AppModule がルートとなり、機能単位のモジュールをまとめて管理する
  • ControllerとServiceを分けて責任を明確にすることで、テストや拡張が容易に

環境変数の管理と設定ファイルの分離

現代のWebアプリケーションでは、「コードに環境依存の値(DBのURLやAPIキーなど)を埋め込まない」ことが基本です。
NestJSでも .env ファイルによってこれを実現できますが、より高度に扱うためには @nestjs/config パッケージの導入が推奨されています。

このセクションでは、開発・本番・テスト環境を意識した環境変数の管理方法と、その活用パターンについて解説します。

@nestjs/config パッケージのインストール

npm install @nestjs/config

これにより、.env ファイルを自動的に読み込み、アプリ全体で共通して使える ConfigService が使えるようになります。

.env ファイルを作成する

まず、プロジェクトルートに .env ファイルを作成し、次のような環境変数を定義してみます。

# .env(開発環境用)
PORT=3000
DATABASE_URL=postgresql://bloguser:blogpass@localhost:5432/blogdb

.env.production.env.test のように、環境ごとにファイルを分けておくと後で便利です。

AppModule で設定モジュールを読み込む

src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
+ import { ConfigModule } from '@nestjs/config'

@Module({
-   imports[],
+   imports: [
+     ConfigModule.forRoot({
+       isGlobal: true,
+       envFilePath: ['.env'],
+     }),
+   ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

isGlobal: true を設定することで、各モジュールで毎回 ConfigModule をインポートしなくても ConfigService を使えるようになります。

ConfigService を使って値を取得する

環境変数が正しく取得できているか確認してみたいと思います。(動作確認後に書いた部分は削除します)

app.service.ts
+ import { ConfigService } from '@nestjs/config'

@Injectable()
export class AppService {
- getHello(): string {
-   return 'Hello World!';
- }
+ constructor(private configService: ConfigService) {}

+ getPort(): string {
+   return this.configService.get<string>('PORT') || '3001';
+ }
}

configService.getを使うことで読み込んだ環境変数の値を使用できます。

src/app.controller.ts
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
-    return this.appService.getHello();
+    return this.appService.getPort();
  }
}

これで http://localhost:3000/ にアクセスすると、PORT の値(3000)が画面に出てきて確認できます。

環境ごとに .env ファイルを切り替える

本番・開発・テストなど、実行環境によって設定を切り替えたい場面は必ず出てきます。
NestJSでは、NODE_ENV 環境変数の値に応じて .env ファイルを出し分けることで、これを柔軟に実現できます。

src/app.module.ts
ConfigModule.forRoot({
  isGlobal: true,
  envFilePath: [
    `.env.${process.env.NODE_ENV}`,
    '.env',
  ],
}),

これにより、実行環境で NODE_ENV を設定すれば自動的に適切な.envが読み込まれます。

# 開発環境
NODE_ENV=development npm run start:dev

# 本番環境(ビルド後)
NODE_ENV=production node dist/main.js

process.env.NODE_ENV はNode.js標準の環境変数です。CI/CDやDocker上でも制御できます。

.env ファイルは Git に含めないようにする(NestJSの既定を活かす)

NestJSのプロジェクトを Nest CLI で生成すると、.gitignore にはすでに次のような .env 関連の設定が追加されています。

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

これによって、以下のような機密性の高い設定ファイルはGitにコミットされないようになっています。

ファイル名 役割の例
.env デフォルトの開発用設定
.env.production.local 本番サーバー固有の秘密設定(DBのURLなど)
.env.local 開発者ごとのローカル個別設定(チーム内で共有しない)

.env.example を追加して共有の指針を示す

実際に運用するうえでは、Gitに含めることを前提にした「ひな形ファイル」を用意しておくのが推奨されます。

# .env.example
PORT=3000
DATABASE_URL=

これにより、他の開発者がプロジェクトをクローンしたときも「どんな環境変数が必要か」が明確になります。

フロントエンドを含めたモノレポ構成に移行

既存のNestJSバックエンドプロジェクトに対し、将来的にフロントエンドを追加できる構成にリファクタリングする方法を紹介します。ここでは モノレポ構成として frontend/backend/ を並べるシンプルな形に整理します。

この構成にしておくことで、

  • フロント・バックの役割が明確になる
  • DockerやCI/CDの設定が整理しやすくなる
  • プロジェクトの拡張性が高まる

などのメリットがあります。

新しいディレクトリ構成イメージ

your-project-root/
├── backend/         # NestJS + Prisma
│   ├── src/
│   ├── prisma/
│   ├── .env
│   └── ...
├── frontend/        # Next.js や React(後ほど追加予定)
│   └── ...          
├── docker/          # 共通のdocker構成(必要に応じて)
├── docker-compose.yml
└── README.md

移行手順(バックエンドを backend/ にまとめる)

backend/ ディレクトリを作成

mkdir backend

NestJSプロジェクトの中身を移動

rsync -av --progress ./ ./backend \
  --exclude .git --exclude .vscode --exclude backend

移動ができたら、いらなくなったファイルの削除

find . -maxdepth 1 ! -name '.' ! -name '..' ! -name '.git' ! -name '.vscode' ! -name 'backend' -exec rm -rf {} +

最後に確認

ls -a

最終的にトップディレクトリがこうなれば完了。

.
..
.git
.vscode
backend

動作確認

cd backend
npm run start:dev

この状態で、ブラウザで http://localhost:3000 にアクセスして、「Hello World!」という文字列が表示されていれば、無事にNestJSアプリのディレクトリ移動は完了です。

おわりに

設定値が正しく取得できているかを一度明示的に確認しておくことは、NestJS に限らずどんな開発でも大切なプロセスです。とくに、環境変数は「取れているつもり」で進めてしまうと、後々原因不明の不具合として表面化することがあります。

今回のように、PORTなどの値を一時的に画面に表示して確認するのは、地味ですが確実な習慣づけのひとつです。そして同時に、設定の取得ロジックを Controller に書くか Service に委ねるかという設計上の判断も、初期段階から意識しておきたい重要なポイントです。

開発初期だからこそ、小さな確認や設計判断を丁寧に行うことが、後の安心と品質につながります。先を急がず、まずは足元を固めながら進めていきましょう。

次回:NestJSの構成と設計原則に向き合う

今回取り上げた環境変数の扱いや、責務を意識した構成は、あくまでスタートラインにすぎません。
チーム開発においては「何をどこに書くべきか」という構造的な判断こそが、長期的な品質と保守性を左右します。

次回は、NestJSの標準的なディレクトリ構成とモジュール設計の考え方を整理しつつ、

  • AppModuleの役割と構成の基準
  • Controller / Service / Moduleの責務と設計分離
  • 将来的な拡張を見据えたスケーラブルな初期設計のあり方

といった実践的なテーマに踏み込んでいきます。

プロジェクトの初期設計における判断が、どのように後の開発スピードとチーム内の理解度に影響するのか。
一つひとつの構成に理由を持たせながら、健全なアーキテクチャの土台を築いていきましょう。

https://shinagawa-web.com/blogs/nestjs-blog-series-crud-and-prisma-intro

Xでシェア
Facebookでシェア
LinkedInでシェア

記事に関するお問い合わせ📝

記事の内容に関するご質問、ご意見などは、下記よりお気軽にお問い合わせください。
ご質問フォームへ

技術支援などお仕事に関するお問い合わせ📄

技術支援やお仕事のご依頼に関するお問い合わせは、下記よりお気軽にお問い合わせください。
お問い合わせフォームへ

弊社の技術支援サービス

お問い合わせ

経営と現場をつなぐ“共創型”の技術支援。
成果に直結するチーム・技術・プロセスを共に整えます。

お問い合わせ