こんにちは!今回は私がやってしまった、Next.jsでよくありそうな開発あるあるを共有したいと思います。APIルート内でNode.jsのAPIを使おうとして「あれ?」となった経験から、サーバーサイドとクライアントサイドの違いについて学んだことをまとめてみました。
どんな失敗をしたの?
APIルート内でファイルアップロードの処理時間を計測しようと思って、何も考えずに書いたコードでこんなエラーに遭遇しました:
Unhandled Runtime Error
TypeError: process.hrtime is not a function
Source: src/utils/index.ts (356:29) @ hrtime
354 | // Create timing manager
355 | const createUploadTiming = (): UploadTiming => ({
> 356 | processStartTime: process.hrtime(),
| ^
357 | uploadStart: new Date()
358 | });
「あれ?おかしいな...」と思いながらコードを見直してみたところ、私の初歩的なミスが明らかになりました。
やっちゃった原因
Next.jsのAPIルートを使っていると、コードがサーバーサイドで動いているのかクライアントサイドで動いているのか、ちょっと混乱することがありますよね。今回の場合:
-
APIルートの特徴
- APIルートのコードは基本的にサーバーサイドで実行される
- でも、そのAPIを呼び出すコードはクライアントサイドで動く
-
今回のミス
- APIを呼び出す側(クライアントサイド)のコードで
process.hrtime()
を使おうとしてた - ブラウザには
process
オブジェクトがないので当然エラーに...😅
- APIを呼び出す側(クライアントサイド)のコードで
process.hrtime()って実はすごい!
そもそもprocess.hrtime()
を使おうとしたのには理由があって、これがNode.jsの高精度タイマーとしてかなり優れているんです:
- [秒, ナノ秒]のタプルを返すので、ナノ秒レベルの精度がある
- システムの単調増加クロックを使用するので、時計の調整に影響されない
-
process.hrtime.bigint()
を使えばナノ秒単位の数値を直接取得できる - Node.js環境では最も高精度な時間計測が可能
...でもブラウザでは使えないんですよね 😅
どう直したの?
調べてみると、performance.now()
という便利なAPIがあることがわかりました:
// Before: うっかりNode.js専用のコードを書いてた...
const timing = {
processStartTime: process.hrtime(), // [秒, ナノ秒]のタプル
uploadStart: new Date()
};
// After: performance.now()を使用
const timing = {
processStartTime: performance.now(), // ミリ秒単位の高精度タイマー
uploadStart: new Date()
};
performance.now()もすごい!
調べてみると、実はperformance.now()
もかなり優れたAPIだということがわかりました:
- マイクロ秒レベルの精度(Date.now()より高精度!)
- システムクロックの変更の影響を受けない
- ブラウザでもNode.jsでも使える(Node.jsの場合は要import)
- 今回の用途(処理の開始時点からの相対時間)で、測定が簡単
⚠️ 注意点:サーバーサイド(APIルート内)でperformance.now()
を使用する場合は、node:perf_hooks
からのインポートが必要です:
import { performance } from 'node:perf_hooks';
Next.jsでの教訓
この経験から、Next.jsを使う上で大事なことに気付きました:
-
実行環境を意識する
- APIルート → サーバーサイド(Node.js環境)
- APIを呼び出すコード → クライアントサイド(ブラウザ環境)
- Server ComponentsとClient Componentsの区別も同様に重要
-
環境固有のAPIの扱い
- Node.js固有のAPIはサーバーサイドでのみ使用
- ブラウザ固有のAPIはクライアントサイドでのみ使用
- 共通のAPIを使う場合も、環境による違いを確認
-
コードの配置を考える
- Node.js専用の処理はAPIルートの中に
- ブラウザ依存の処理は適切にクライアントコンポーネントに
- 必要に応じて環境チェックを実装
まとめ
恥ずかしい失敗ではありましたが、これをきっかけにNext.jsでの実行環境の違いをより意識するようになりました。特にApp RouterやServer Componentsを使う場合は、コードがどこで実行されるのかを常に意識することが大切だと学びました。
似たような「あれ?」を経験した方や、これから経験しそうな方の参考になれば幸いです。Next.jsの開発では、サーバー/クライアントの境界を意識することで、より堅牢なアプリケーションが作れるはずです。
このブログは https://tech.jugoya.ai/ で公開している Next.js プロジェクトの開発過程で得られた知見の一つです。他の記事やソースコードも公開していますので、ぜひご覧ください!
ちなみに、より詳しく知りたい方は以下のドキュメントもチェックしてみてください:
皆さんも似たような経験がありましたら、ぜひコメントで教えてください!