Next.js(App Router)でMSWを使用する
tl;dr
コードを読めばわかるという人はこちらをご参照のほど。
前提知識
MSWとは
WebアプリケーションにはWeb Workerという仕組みがあり、UIとは別スレッドで動いてバックグラウンド処理を行う。その1つがブラウザ・サーバー間の通信をプロキシするService Workerであり、MSWはこの仕組みを使ってブラウザからの通信を受け取りダミーの値を返す。
Instrumentation
Next.jsでは、監視ツールなどのミドルウェアを起動するためにInstrumentationの機能を使用することができる。
気をつけることとしては2点ある。
- 起動スクリプトは必ずinstrumentation.js|tsというファイル名にして、プロジェクトのルートか
src
ディレクトリに配置する必要がある - 実験段階の機能であるため、[next.config.jsで機能を有効にする])https://nextjs.org/docs/app/api-reference/next-config-js/instrumentationHook)必要がある
Next.jsのランタイム
Next.jsのアプリケーション内では、Node.jsとEdgeの2つのランタイムを認識できる。(公式ドキュメント)
MSWでサーバーコンポーネント側のAPI通信をモックするモジュールはEdgeランタイムでは動作しないため、設定する際にこれを考慮した実装にする必要がある。
手順
基本的にはMSWの公式ドキュメントに従う。
この辺やこの辺も見ると良いかも。
まずMSWをインストールする。(npmやyarnなど他のパッケージマネージャーのコマンドは割愛。)
pnpm add --save-dev msw
mswコマンドを使ってService Workerの登録をする。(コマンド提供してくれてるのむちゃくちゃ助かる...)
クライアントコンポーネント側のモックをする場合は必須。
pnpm exec msw init public --save
これでpublic
ディレクトリにmockServiceWorker.js
というファイルができるとともに、package.json
に以下の記述が増えているはず。
{
...
"devDependencies": {
+ "msw": "^2.3.5",
...
+ "msw": {
+ "workerDirectory": ["public"]
+ }
}
次に、モックの定義をする。
import { http, HttpResponse, type RequestHandler } from "msw";
const helloHandler = http.get("https://example.com/hello", () => {
return HttpResponse.json({
message: "Hello, world!",
});
});
export const handlers: RequestHandler[] = [helloHandler];
そしてモックを起動するスクリプトを書く。
import { setupWorker } from "msw/browser";
import { handlers } from "../handlers";
export const worker = setupWorker(...handlers);
import { setupServer } from "msw/node";
import { handlers } from "../handlers";
export const server = setupServer(...handlers);
async function initMocks() {
if (typeof window === "undefined") {
const { server } = await import("./server");
server.listen();
} else {
const { worker } = await import("./browser");
worker.start();
}
}
export { initMocks };
export { initMocks } from "./setup";
最後に、Instrumentationの設定をする。
/** @type {import('next').NextConfig} */
const nextConfig = {
+ experimental: {
+ instrumentationHook: true,
+ },
};
export async function register() {
// "msw/node"がNode.jsランタイムでのみ利用可能(=Edgeランタイムで利用不可)
if (process.env.NEXT_RUNTIME === "nodejs") {
const { initMocks } = await import("./lib/msw");
initMocks();
}
}
これで設定は以上。🎉
あとはアプリケーション内でfetch APIを使えばモックが返ってくる。
この記事を執筆するモチベーション
Next.js+〇〇ライブラリという組み合わせのリポジトリを公式がたくさん公開しているが、執筆時点ではPages Router用のコードしかなくて困ったので。。。
Discussion