NodeJS 速習チュートリアル

Node.js Express.js

1. Express.jsとは?

Express.js(または単にExpress)は、WebアプリケーションやAPIを構築するために設計された、最もポピュラーなNode.js Webアプリケーションフレームワークです。
Node.jsにおける「デファクトスタンダード」のサーバーフレームワークとして広く認知されています。

主な特徴:

  • ミニマルかつ柔軟: 必要最小限の機能を提供し、自由度が高い。
  • アンオピニオンネイテッド(Unopinionated): アプリケーションの構造を開発者が自由に決定できる。
  • 軽量で高速: オーバーヘッドが少なく、パフォーマンスに優れる。
  • ミドルウェアによる拡張性: 豊富なミドルウェアを組み合わせて機能を拡張可能。
  • 巨大なエコシステム: 膨大な数のプラグインや拡張機能が存在する。

2. なぜExpress.jsを選ぶのか?

Expressは、Node.jsの機能を隠蔽することなく、Webアプリケーションに必要な基本機能の薄いレイヤーを提供します。

提供される主な機能:

  • 堅牢なルーティング(Routing)システム
  • HTTPヘルパー(リダイレクト、キャッシングなど)
  • HTTPリクエストに応答するためのミドルウェアサポート
  • 動的なHTMLレンダリングのためのテンプレートエンジン
  • エラーハンドリング用ミドルウェア

3. Expressを始める

ExpressはあらゆるNode.jsプロジェクトに追加できます。新しいExpressアプリケーションの開始方法は以下の通りです。

3.1 前提条件

開始する前に、以下が準備されていることを確認してください:

  • Node.jsのインストール(v14.0.0以降を推奨)
  • npm(Node.jsに付属)または yarn
  • コードエディタ(VS Code, WebStormなど)

3.2 Expressのインストール

Node.jsアプリケーションでExpressを使用するには、まずインストールが必要です:

npm install express

Expressをインストールし、package.jsonの依存関係(dependencies)に保存する場合:

npm install express --save

4. Hello Worldの例

Expressを使用したシンプルな「Hello World」アプリケーションを作成してみましょう。
この例は、Expressアプリケーションの基本的な構造を示しています。

主要な構成要素:

  • Expressモジュールのインポート
  • Expressアプリケーションインスタンスの作成
  • ルートの定義
  • サーバーの起動
const express = require('express');
const app = express();
const port = 8080;

// ルートURLへのGETリクエストに対するルートを定義
app.get('/', (req, res) => {
  res.send('ExpressからのHello World!');
});

// サーバーの起動
app.listen(port, () => {
  console.log(`サンプルアプリが http://localhost:${port} で待機中です`);
});

このコードをapp.jsという名前で保存し、Node.jsで実行します:
node app.js
その後、ブラウザで http://localhost:8080 にアクセスすると、「Hello World」メッセージが表示されます。

5. 基本的なルーティング(Basic Routing)

ルーティングとは、アプリケーションが特定のエンドポイント(URI)へのクライアントリクエストに対して、異なるHTTPメソッド(GET, POST, PUT, DELETEなど)を使用してどのように応答するかを指します。

Expressは、HTTPメソッドに対応するルートを定義するためのシンプルなメソッドを提供します:

  • app.get() - GETリクエストを処理
  • app.post() - POSTリクエストを処理
  • app.put() - PUTリクエストを処理
  • app.delete() - DELETEリクエストを処理
  • app.all() - すべてのHTTPメソッドを処理
const express = require('express');
const app = express();
const port = 8080;

// ルートパスへのGETリクエストに応答
app.get('/', (req, res) => {
  res.send('ホームページへのGETリクエスト');
});

// ルートパスへのPOSTリクエストに応答
app.post('/', (req, res) => {
  res.send('ホームページへのPOSTリクエスト');
});

// /aboutパスへのGETリクエストに応答
app.get('/about', (req, res) => {
  res.send('アバウトページ');
});

// 他のすべてのルートをキャッチ
app.all('*', (req, res) => {
  res.status(404).send('404 - ページが見つかりません');
});

app.listen(port, () => {
  console.log(`サーバーが起動しました: http://localhost:${port}`);
});

6. ルートパラメータ(Route Parameters)

ルートパラメータは、URL内の特定の値をキャプチャする名前付きのURLセグメントです。
パス内でコロン : プレフィックスを付けて指定します。
例:/users/:userId/books/:bookId
この例では、userIdbookId がルートパラメータであり、req.params を介してアクセスできます。

const express = require('express');
const app = express();
const port = 8080;

// パラメータを含むルート
app.get('/users/:userId/books/:bookId', (req, res) => {
  // req.paramsを使用してパラメータにアクセス
  res.send(`ユーザーID: ${req.params.userId}, 本ID: ${req.params.bookId}`);
});

app.listen(port, () => {
  console.log(`サーバーが実行中: http://localhost:${port}`);
});

7. クエリパラメータ(Query Parameters)

クエリパラメータは、URLの ? の後に続くキーと値のペアです。
Expressによって自動的にパースされ、req.query で利用可能になります。
例:http://example.com/search?q=express&page=2
このURLでは、q=expresspage=2 がクエリパラメータであり、req.query.q および req.query.page としてアクセスできます。

const express = require('express');
const app = express();
const port = 8080;

// クエリパラメータを処理するルート
app.get('/search', (req, res) => {
  // req.queryを使用してクエリパラメータにアクセス
  const { q, category } = req.query;
  res.send(`検索クエリ: ${q}, カテゴリ: ${category || 'なし'}`);
});

app.listen(port, () => {
  console.log(`検索APIが待機中: http://localhost:${port}`);
});

8. Expressにおけるミドルウェア

ミドルウェア関数は、Expressアプリケーションのバックボーンです。
以下の要素にアクセスできます:

  • リクエストオブジェクト (req)
  • レスポンスオブジェクト (res)
  • スタック内の次のミドルウェア関数 (next)

ミドルウェアができること:

  • 任意のコードの実行
  • リクエストおよびレスポンスオブジェクトの変更
  • リクエスト・レスポンスサイクルの終了
  • スタック内の次のミドルウェアの呼び出し

8.1 組み込みミドルウェア

Expressには、いくつかの便利な組み込みミドルウェアが含まれています:

  • express.json() - JSONリクエストボディをパース
  • express.urlencoded() - URLエンコードされたリクエストボディをパース
  • express.static() - 静的ファイルを配信
  • express.Router() - モジュール化されたルートハンドラを作成
const express = require('express');
const app = express();
const port = 8080;

// JSONリクエストボディをパースするためのミドルウェア
app.use(express.json());

// URLエンコードされたリクエストボディをパースするためのミドルウェア
app.use(express.urlencoded({ extended: true }));

// publicディレクトリから静的ファイルを配信するためのミドルウェア
app.use(express.static('public'));

// JSONミドルウェアを使用するPOSTルート
app.post('/api/users', (req, res) => {
  // req.bodyにパースされたJSONデータが含まれる
  console.log(req.body);
  res.status(201).json({ message: 'ユーザーが作成されました', user: req.body });
});

app.listen(port, () => {
  console.log(`ミドルウェアデモ実行中: http://localhost:${port}`);
});

9. Expressにおけるエラーハンドリング

Expressのエラーハンドリングは、4つの引数 (err, req, res, next) を持つ特別なミドルウェア関数を通じて行われます。

ポイント:

  • エラーハンドリングミドルウェアは必ず4つの引数を持つ必要がある
  • 他の app.use() やルート呼び出しの後に定義する必要がある
  • 複数のエラーハンドリングミドルウェアを定義可能
  • next(err) を使用してエラーを次のハンドラに渡す
const express = require('express');
const app = express();
const port = 8080;

// エラーを投げる可能性のあるルート
app.get('/error', (req, res) => {
  // エラーのシミュレーション
  throw new Error('何かがうまくいきませんでした!');
});

// 非同期コードでnext(error)を使用するルート
app.get('/async-error', (req, res, next) => {
  // 失敗する非同期操作のシミュレーション
  setTimeout(() => {
    try {
      // 失敗する可能性のある処理
      const result = nonExistentFunction(); // ここでエラーが発生
      res.send(result);
    } catch (error) {
      next(error); // エラーをExpressに渡す
    }
  }, 100);
});

// カスタムエラーハンドリングミドルウェア
// エラーハンドラーとして認識されるために4つのパラメータが必要
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('サーバーエラーが発生しました!');
});

app.listen(port, () => {
  console.log(`エラーハンドリングデモ: http://localhost:${port}`);
});

10. 静的ファイルの配信

Expressは、組み込みの express.static ミドルウェアを使用して、画像、CSS、JavaScriptなどの静的ファイルを配信できます。

ベストプラクティス:

  • 静的ファイルは専用のディレクトリ(通常は public または static)に配置する
  • ルート定義の前に静的ミドルウェアをマウントする
  • 本番環境ではパフォーマンス向上のために CDN の使用を検討する
  • 静的アセットに適切なキャッシュヘッダーを設定する
const express = require('express');
const path = require('path');
const app = express();
const port = 8080;

// 'public'ディレクトリから静的ファイルを配信
app.use(express.static('public'));

// 仮想パスプレフィックスを指定することも可能
app.use('/static', express.static('public'));

// 絶対パスの使用(推奨)
app.use('/assets', express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
  res.send(`
    <h1>静的ファイル配信の例</h1>
    <img src="/images/logo.png" alt="ロゴ">
    <link rel="stylesheet" href="/css/style.css">
    <script src="/js/script.js"></script>
  `);
});

app.listen(port, () => {
  console.log(`静的ファイルサーバー: http://localhost:${port}`);
});

11. 別ファイルでのルーティング

構成を整理するために、Express Router を使用してルートを別々のファイルに定義できます。

routes/users.js

const express = require('express');
const router = express.Router();

// このルーター専用のミドルウェア
router.use((req, res, next) => {
  console.log('ユーザー・ルーターの時刻:', Date.now());
  next();
});

router.get('/', (req, res) => {
  res.send('ユーザーホームページ');
});

router.get('/:id', (req, res) => {
  res.send(`ID: ${req.params.id} のユーザープロファイル`);
});

module.exports = router;

app.js (メインファイル)

const express = require('express');
const usersRouter = require('./routes/users');
const app = express();
const port = 8080;

// ルーターの使用
app.use('/users', usersRouter);

app.get('/', (req, res) => {
  res.send('メインアプリケーションのホームページ');
});

app.listen(port, () => {
  console.log(`モジュール化ルーティング実行中: http://localhost:${port}`);
});

12. テンプレートエンジン

Expressは、動的なHTMLを生成するためにテンプレートエンジンを設定できます。

const express = require('express');
const app = express();
const port = 8080;

// ビューエンジンをEJSに設定
app.set('view engine', 'ejs');

// テンプレートが配置されているディレクトリを設定
app.set('views', './views');

app.get('/', (req, res) => {
  const data = {
    title: 'Expressテンプレートの例',
    message: 'EJSからのこんにちは!',
    items: ['アイテム 1', 'アイテム 2', 'アイテム 3']
  };

  // views/index.ejsテンプレートをレンダリング
  res.render('index', data);
});

app.listen(port, () => {
  console.log(`テンプレートデモ実行中: http://localhost:${port}`);
});

※ 使用には npm install ejs が必要です。

13. Express Application Generator

Express Application Generator は、Expressアプリケーションのスケルトンを迅速に作成するためのツールです。

主な特徴:

  • 適切に構造化されたアプリケーションを作成
  • 開発環境のセットアップ
  • 一般的なミドルウェアの設定
  • エラーハンドリングの組み込み
  • 各種テンプレートエンジンのサポート

使用方法:

# ジェネレーターをグローバルにインストール
npm install -g express-generator

# 新しいExpressアプリケーションを作成
express --view=ejs myapp

# ディレクトリに移動してインストール・起動
cd myapp
npm install
npm start

14. Express.js ベストプラクティス

堅牢でメンテナンス性の高いExpressアプリケーションを構築するためのベストプラクティスです:

  • プロジェクト構造: 機能やコンポーネントごとにコードを整理する。
  • 環境変数: 設定には dotenv を使用する。
  • エラーハンドリング: エラーハンドリングを集中管理する。
  • ロギング: morganwinston などのロギングライブラリを使用する。
  • セキュリティ: Helmet やレート制限(rate limiting)などのセキュリティ対策を実装する。
  • バリデーション: express-validator などのライブラリを使用して入力を検証する。

14.1 セキュリティのベストプラクティス

const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const app = express();

// セキュリティミドルウェア(HTTPヘッダーの設定)
app.use(helmet());

// CORSの設定
app.use(cors({
  origin: 'https://example.com',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

14.2 パフォーマンスのベストプラクティス

  • compression ミドルウェアを使用してレスポンスを圧縮する。
  • 適切なキャッシング戦略を実装する。
  • 本番環境では Nginx などのリバースプロキシを前面に配置することを検討する。
  • Clustering を使用してマルチコアシステムを最大限に活用する。
  • データベースクエリを最適化する。