【ITニュース解説】Caching in Node.js with TypeScript using globalThis and declare global
2025年09月27日に「Dev.to」が公開したITニュース「Caching in Node.js with TypeScript using globalThis and declare global」について初心者にもわかりやすく解説しています。
ITニュース概要
Node.jsとTypeScriptで、サーバー稼働中に一時的にデータを保存する軽量なキャッシュ機能を紹介する。`globalThis`を使うことで、外部APIへの重複アクセスを防ぎ、処理を高速化できる。TypeScript環境では、`declare global`で型を明示的に宣言し、安全に利用する方法を解説する。
ITニュース解説
システム開発において、特にNode.jsのようなサーバーサイドJavaScript環境でTypeScriptを併用する場合、データの効率的な管理は非常に重要である。サーバーが稼働している間、特定のデータを一時的に保持しておき、必要に応じて再利用する仕組みを「キャッシュ」と呼ぶ。このキャッシュを適切に利用することで、システムのパフォーマンス向上や、外部システムへの無駄な問い合わせを減らすことができる。
例えば、あるプロジェクトでワークショップの予約情報を管理している状況を考える。それぞれの予約記録には「車両ID」が含まれており、このIDは別の外部システムに登録されている車両の詳細情報と紐づいている。もし、第三者システムが予約データと車両データの両方を同時に取得したい場合、私たちのシステムは予約情報を受け取った後、その予約に紐づく車両IDを使って外部システムから車両データを取得し、予約情報と統合して返す必要がある。
このとき、特に注意すべきは「同じ車両データを何度も外部システムから取得しない」という点である。車両データは頻繁に変わるものではないため、一度取得したデータを一時的に保存しておけば、次回以降の同じ車両データの要求に対しては、外部システムに再度問い合わせることなく、保存しておいたデータをすぐに提供できる。これは、ネットワークリソースの節約にもなり、API呼び出しにかかる時間を短縮し、結果的にシステム全体の応答速度を向上させる。このような目的のために、シンプルで軽量なキャッシュ機構が必要になる。
このキャッシュを実現する一つの効率的な方法として、「globalThis」というJavaScriptの特別なオブジェクトと、TypeScriptの「declare global」構文を組み合わせる方法がある。
まず、「globalThis」について説明する。JavaScriptでは、コードが実行される環境(WebブラウザやNode.jsサーバーなど)ごとに、「グローバルスコープ」という概念が存在する。グローバルスコープとは、プログラム全体からアクセスできる最も広範な領域であり、ここに定義された変数や関数は、どこからでも利用できる。globalThisは、このグローバルスコープにアクセスするための標準的なオブジェクトである。つまり、Webブラウザ環境であればwindowオブジェクト、Node.js環境であればglobalオブジェクトに相当するものが、環境を問わずglobalThisとして統一的に参照できる。globalThisに何らかの変数を設定すると、その変数はサーバープロセスが稼働している間ずっとメモリ上に存在し続け、アプリケーションのどの部分からもアクセス可能になる。
例えば、globalThis.myAppName = "DriveMotive";というコードを実行すると、myAppNameという変数がグローバルスコープに登録され、どこからでもglobalThis.myAppNameとして「DriveMotive」という値を取得できるようになる。この特性は、サーバーが停止するまで消えないキャッシュデータを保持するのに非常に適している。
しかし、TypeScriptを使う場合、globalThisを直接扱うと一つ問題が発生する。TypeScriptは、JavaScriptに「型」の概念を導入し、コードの安全性を高めるための言語である。型システムにより、変数がどのような種類のデータを持つかを事前に定義することで、プログラムが実行される前に多くのエラーを発見できる。そのため、もしglobalThis.__vehicleCache = new Map();というコードを書こうとすると、TypeScriptは「globalThis型には__vehicleCacheというプロパティは存在しません」というエラーを出す。これはTypeScriptが、開発者が意図しないプロパティの追加を防ぎ、予期せぬ動作を避けるための保護機能が働いているためである。TypeScriptは、私たちが__vehicleCacheというプロパティを追加しようとしていることを事前に知らないため、エラーを報告するのだ。
このTypeScriptの型チェックをパスし、__vehicleCacheというグローバルなキャッシュ変数を安全に利用するためには、「declare global」という構文と、モジュールとしてのファイルの宣言が必要になる。
最初のステップとして、そのファイルが「モジュール」であることをTypeScriptに明示的に伝える必要がある。JavaScriptやTypeScriptのファイルは、特別な記述がない場合、グローバルスコープに影響を与えるスクリプトとして扱われることがある。しかし、export {};という空のexport文をファイルの先頭に追加することで、そのファイルが独立したモジュールであることをTypeScriptに認識させることができる。これにより、そのファイル内で宣言された変数が安易にグローバルスコープを汚染するのを防ぎつつ、グローバルな宣言を適切に扱う準備が整う。
次に、「declare global」構文を使って、TypeScriptに__vehicleCacheというグローバル変数の存在と、その型を伝える。具体的には、declare global { var __vehicleCache: Map | undefined; }と記述する。この記述は、TypeScriptに対して「アプリケーションのどこかに__vehicleCacheという名前のグローバル変数が存在する可能性があり、その変数はMapオブジェクトであるか、あるいはまだ定義されていない(undefinedである)可能性がある」と教えていることになる。これにより、TypeScriptはglobalThis.__vehicleCacheという記述をエラーとして扱わず、型安全な方法でグローバル変数を扱えるようになる。
これらの準備が整ったら、実際にキャッシュを初期化し、利用する。キャッシュの初期化には、const vehicleCache = (globalThis.__vehicleCache ??= new Map());という簡潔な一行のコードが非常に便利である。このコードは、以下のような処理を同時に実行する。まず、globalThis.__vehicleCacheがすでに存在するかどうかを確認する。もし__vehicleCacheがすでにglobalThisに設定されており、その値がnullやundefinedでない場合は、その既存の値をvehicleCache定数に割り当てる。一方、もし__vehicleCacheがglobalThisにまだ存在しない(またはnull/undefinedである)場合は、新しくMap()オブジェクトを作成し、それをglobalThis.__vehicleCacheに設定すると同時に、その新しく作成したMapオブジェクトをvehicleCache定数に割り当てる。
この??=演算子(Nullish Coalescing Assignment)を使うことで、キャッシュがまだ作成されていない場合にのみ新しいMapが生成され、その後は常に既存のキャッシュインスタンスが再利用されることが保証される。これにより、サーバーが起動してから一度だけキャッシュが初期化され、それ以降は常に同じMapオブジェクトを通じて車両データを保存・取得できるようになる。
このように、globalThisとdeclare globalを組み合わせることで、Node.jsとTypeScript環境において、サーバーのライフサイクル全体を通じて永続する、シンプルでありながらも強力なキャッシュメカニズムを安全かつ効率的に実装できる。これは、外部APIへの依存度が高いアプリケーションや、頻繁に利用される静的なデータを効率的に管理したい場合に、非常に有効なアプローチとなるだろう。