New Relic では、常に開発者のニーズに対応し、革新的なツール、フレームワーク、ライブラリとの統合を図っています。NestJS は、効率的で信頼性が高く、スケーラブルなサーバーサイドアプリケーションの構築を支援する、多くの新しい Node.js フレームワークの 1 つです。これまで、NestJS は TypeScript を使用しており、定義済みのビルドプロセスを使用して ES5 にトランスパイルされるため、New Relic One に統合することは容易ではありませんでした。

しかし、New Relic One observability platform では、Node.js エージェントが提供されており、これを利用すれば、弊社のエージェントとコードサンプルで NestJS を迅速に統合することが可能です。

New Relic Node.js APM エージェント

スケーラビリティというと、マイクロサービスがすぐに思い浮かびます。NestJSは、モジュール性、拡張性、汎用性を提供しますが、それに伴い、アプリケーションのパフォーマンスを観察することが複雑になっています。ここで、このNestJSの統合の出番となります。

このNode.jsエージェントは、Nodeアプリケーションの健全性を表示し、パフォーマンスを測定し、エラーを突き止めることができるだけでなく、サービスマップによってマイクロサービスの関係を可視化し、ディストリビューティッド(分散)トレーシングによってサービス間のリクエストを追跡することが可能です。

Node.jsエージェントとNestJSでマイクロサービスの可観測性を実現する

NestJS と New Relic Node.js エージェントの統合を実証するために、このサンプルアプリを使用します。これは、NestJS を使って書かれた 2 つの Node.js アプリケーションを含む Docker 化されたサービスです。両方のアプリには、NestJS インターセプターの概念を使用して統合された New Relic Node.js エージェントのインスタンスがあります。

親アプリが子インターセプターを呼び出すと、両方のアプリが新しいウェブトランザクションを開始します。アプリ間のリクエストが完了すると、New Relic One 内で利用できるディストリビューティッド(分散)トレーシングで、マイクロサービス間のリクエストのフルパスがわかります。次の画像は、表示されるであろうパスの例を示しています。

New Relic Node.js エージェントを使用した NestJS の統合

最新の JavaScript パターンを使用し、あらかじめ定義されたビルドプロセスを使用して TypeScript から ES5 にトランスパイルされるため、NestJS を New Relic One と統合する際には、いくつかの重要な注意点が存在します。

ファイル拡張子(.ts)

NestJSはTypeScriptを使用しているため、Agentが使用するnewrelic.ts(js)設定ファイルは、標準の.js拡張子ではなく、.ts拡張子で保存することを覚えておくことが重要です。

/src ディレクトリ

通常、newrelic.ts の設定ファイルは、プロジェクトのルートディレクトリに置くことが推奨されます。NestJSでは、/srcディレクトリがアプリケーションのルートディレクトリとして扱われます。これは、エントリポイントであるmain.tsファイルを含んでいるからです。そのため、newrelic.tsファイルを/srcに配置することを忘れないでください。

NestJSインターセプター

次のコードでは、サービスが呼び出されるたびに New Relic エージェントのトランザクションを開始および終了する NestJS インターセプターの実装例を見ることができます。

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelic = require('newrelic');
​
@Injectable()
export class NewrelicInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return newrelic.startWebTransaction(context.getHandler().name, function () {
      const transaction = newrelic.getTransaction();
​
      return next.handle().pipe(
        () => transaction.end());
    });
  }
}

Interceptors は HTTP リクエストを傍受し、この実装では New Relic One にデータを送信します。

この例で分析してみましょう。

  1. 9行目でnewrelicモジュールをrequireします。
  2. アプリケーションに HTTP リクエストが送信されるたびに、インターセプターが起動します。
  3. 14行目で、newrelic モジュールはウェブトランザクションを開始します。また、 startWebTransaction第2引数として、無名関数が渡されているのが確認できます。
  4. この無名関数はハンドラー関数で、計装する関数を定義しています。
  5. 15行目で、現在のNew Relic/NestJSのトランザクションを取得します。
  6. 17行目で、NestJSのコールハンドラーであるnextを使います。
  7. キャプチャされたHTTPリクエストが呼び出し元に戻ってきたとき、next.handle() を呼び出します。
  8. このときHTTPリクエスト(トランザクション)が完了したことがわかります。New Relicエージェントにトランザクションが完了したことを知らせるために、18行目で transaction.end() を使います。

サンプルアプリケーション

NestJS と New Relic で遊んでみたい方は、nestjs-test-app GitHub リポジトリにアクセスし、詳細と手順をご覧ください。このリポジトリには、NestJS アプリケーションと New Relic One を統合するために必要なものがすべて揃っているため、JavaScript と observability の両方の世界における最新技術から恩恵を受けることができます。ご質問がある場合は、リポジトリの管理者にお気軽にお問い合わせください。