Hapi.js徹底解説:アーキテクチャ・プラグイン・バリデーションから運用までの実践ガイド

はじめに — Hapi.jsとは何か

Hapi.js(一般にはhapiと表記される)は、Node.js向けの高機能なHTTPサーバーフレームワークです。堅牢な設定ベースの設計、プラグインシステム、リクエストライフサイクルの明確化、スキーマバリデーションとの密接な統合が特徴で、大規模なAPIやオンラインサービスのバックエンドで採用されることが多いフレームワークです。HapiはもともとWalmart Labsのプロジェクトとして開発され、オープンソースとしてコミュニティでメンテナンスされています。

設計理念と特徴

Hapiの設計は「設定(configuration)中心」と「プラグインにより機能を組み立てる」ことを基本にしています。ミドルウェアチェーンに依存するExpressとは異なり、Hapiは明確なリクエストライフサイクルと拡張ポイント(extension points)を用意し、各機能をプラグインとして追加・分離しやすくしています。これにより、大規模チームでの責務分離やテスト容易性が高くなります。

  • 豊富なプラグインエコシステム(認証、ドキュメンテーション、静的配信など)
  • Joiによる宣言型バリデーションの標準化
  • 詳細なリクエスト/レスポンス操作用のToolkit(h)
  • server.injectを使った容易なユニットテスト

基本的な使い方(サーバ起動とルーティング)

Hapiサーバの基本はサーバの生成、ルートの登録、サーバ起動の3つです。典型的なコードは以下のようになります。

const Hapi = require('@hapi/hapi');

const init = async () => {
  const server = Hapi.server({ port: 3000, host: 'localhost' });

  server.route({
    method: 'GET',
    path: '/',
    handler: (request, h) => {
      return 'Hello Hapi!';
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

init();

ポイントは、handlerがリクエスト情報を含むrequestオブジェクトと、レスポンス操作用のh(Toolkit)を受け取る点です。Toolkitはレスポンスの型変換、リダイレクト、コード設定などを一貫して行えます。

ルート定義とオプション

server.routeではパスやメソッド以外に、入力スキーマ(validate)、認可(auth)、レスポンスの構造(response)やキャッシュ(cache)、プラグイン固有の設定(plugins)などを細かく指定できます。たとえば、Joiを用いたバリデーションは以下のように統合されます。

const Joi = require('joi');

server.route({
  method: 'POST',
  path: '/users',
  options: {
    validate: {
      payload: Joi.object({
        name: Joi.string().required(),
        email: Joi.string().email().required()
      })
    }
  },
  handler: async (request, h) => {
    // バリデーション済みのpayloadを使用
  }
});

バリデーション: Joiとの連携

HapiはJoi(Joiはhapiエコシステムで生まれ、現在は独立している)を標準的なバリデーションツールとして採用してきました。宣言型でスキーマを定義し、リクエストのpayload、params、query、headersなどに対してバリデーションを自動で適用できます。バリデーション失敗時のエラーハンドリングもカスタマイズ可能です。

プラグインシステムと拡張ポイント

Hapiの強みはプラグインアーキテクチャです。プラグインは機能をパッケージングし、サーバに登録することで機能を追加します。プラグインは自身の設定スコープを持ち、ログ、ルート、デコレータ(serverやrequestにメソッドを追加する機能)、イベントフック(server.ext)などを提供できます。企業でマイクロサービス的に機能を切り出す際に役立ちます。

  • 代表的な公式プラグイン: inert(静的ファイル配信)、vision(テンプレートレンダリングサポート)
  • 認証関連: hapi-auth-cookie(セッション)、@hapi/jwtやhapi-auth-jwt2(JWT)、bell(OAuth)
  • ドキュメント: hapi-swagger によるOpenAPI生成

認証と認可

Hapiでは「戦略(strategy)」を登録して認証を行います。戦略はプラグインとして提供され、多様な方式(cookie、JWT、OAuthなど)を簡単に差し替えられます。routeごとに認証の有無や認可ロールを設定でき、シンプルかつ柔軟なアクセス制御が可能です。

リクエストライフサイクルと拡張ポイント

Hapiはリクエストが来てからレスポンスを返すまでのライフサイクルを明示的に定義しており、各フェーズに対して拡張(onRequest, onPreAuth, onPostAuth, onPreHandler, onPostHandler, onPreResponseなど)を設定できます。この構造により、クロスカッティングな処理(ロギング、ヘッダー加工、共通レスポンス変換など)を適切なフェーズで実装できます。

テストとserver.inject()

Hapiはserver.injectというインターナル的なリクエスト注入APIを持ち、外部にHTTPサーバを立ち上げることなくルート単位でのテストが可能です。これによりユニットテストや統合テストが高速かつ分かりやすく実装できます。

パフォーマンスとスケーラビリティ

Hapi自体はNode.jsのイベントループモデル上で動作するため、I/Oバウンドな処理に強い設計です。パフォーマンス最適化にあたっては、以下の点を考慮します。

  • 非同期処理をasync/awaitやPromiseで適切に扱う
  • 不要な同期処理を避ける(重いCPU処理はワーカープロセスへオフロード)
  • キャッシュ(response.cacheや外部キャッシュ)やCDNの活用
  • スケールアウト時はプロセスマネージャ(PM2等)やクラスタリング、Kubernetesで水平スケール

運用上のベストプラクティス

実運用におけるポイントは次の通りです。

  • 構成は環境変数や設定ファイルで分離し、環境ごとの挙動を明確にする
  • プラグイン単位で責務を分離し、モジュール化を進める(テストのしやすさ向上)
  • ログは構造化(JSON)で出力し、集約して監視/アラートを行う
  • 依存(特にセキュリティ関連プラグイン)は定期的にアップデートし脆弱性を監視する
  • OpenAPI(hapi-swagger等)でAPI仕様を自動生成し、CIに組み込む

よくある落とし穴と注意点

Hapiを使う際の留意点としては、プラグインの互換性(Hapi本体のメジャーバージョンアップに伴うAPI変更)や、過度なサーバ側の責務集中を避けることが挙げられます。バージョンアップ時はリリースノートを確認し、プラグインが新しいコアAPIに対応しているかをチェックする必要があります。

Expressとの比較(簡潔に)

Expressはミニマルで学習コストが低く、小〜中規模のアプリケーションに適しています。一方Hapiは構成指向で機能が標準化されており、大規模開発・チーム開発での整合性や拡張性に優れます。選択はプロジェクトの規模、チーム体制、既存のエコシステムによります。

実践的な導入フロー

Hapiを新規プロジェクトに導入する際の一例のステップは以下の通りです。

  • 最小構成でサーバを立ち上げ、基本的なルートとエラーハンドリングを実装する
  • 認証戦略と入力バリデーション(Joi)を早めに導入する
  • 機能単位でプラグイン化し、テストを追加する(server.injectを活用)
  • ログ、メトリクス、ヘルスチェックを組み込み運用基盤を整備する
  • CI/CDにデプロイとスキーマ(OpenAPI)生成を組み込む

まとめ

Hapi.jsは「設定とプラグインで堅牢に組み立てる」スタイルを持ち、特に大規模プロジェクトや複数チームが関与する開発で有効です。明確なリクエストライフサイクル、宣言的なバリデーション、豊富なプラグイン群により、生産性と保守性を高めることができます。一方で本番導入時はプラグイン互換性やバージョン管理に注意し、運用面の設計を先に進めることが重要です。

参考文献

Hapi公式ドキュメント — hapi.dev

Hapi GitHubリポジトリ

Joi公式ドキュメント

inert(静的ファイル配信プラグイン)

vision(テンプレートサポート)

hapi-swagger(OpenAPI生成)

hapi-auth-cookie(認証プラグイン)