MSW2.0(Mock Service Worker)の使い方 ~②実践編~

2024/4/10

msw

React

swr

はじめに

本記事は、MSW2.0(Mock Service Worker)の使い方 ~①環境構築編~ の続きとなりまして、mswの基本的な使い方を紹介しています。

環境構築を終えている前提で進みますので、まだ目を通していない方は、上記記事を先にご確認ください。

学べる事

  • mswの基本的な使い方
  • mswの実践的な使い方

動作環境

  • Node: v20.7.0
  • Vite: ^5.2.0
  • React(TypeScript): ^18.2.0
  • msw: ^2.2.3
  • swr: ^2.2.5

ソースコード

今回説明に作成したサンプルアプリはGithubにて公開しています。

https://github.com/kentechn/react-msw-sample-app

基本的な使い方

今回は、以下apiのモック作成を通じて、使い方を解説していきます。

  • GET /api/users -> ユーザーリストの取得
  • GET /api/users/[userId] -> userIdに紐づくユーザーの取得
  • POST /api/users -> ユーザーの新規作成

GETリクエストのモック定義方法

import { HttpResponse, http } from "msw";

type User = {
  id: number;
  name: string;
};

const userList: User[] = [
  { id: 1, name: "テストユーザー1" },
  { id: 2, name: "テストユーザー2" },
  { id: 3, name: "テストユーザー3" },
  { id: 4, name: "テストユーザー4" },
];

export const handlers = [
  // ユーザー一覧取得api
  http.get("/api/users", () => {
    return HttpResponse.json(userList);
  })
];

パスパラメータの取得

userListの中から、パスパラメータのuserIdに紐づくユーザーをレスポンスとして返します

export const handlers = [
  ...,
  // 特定のユーザー取得api
  http.get("/api/users/:userId", ({params}) => {
    const userId = Number(params.userId)
    const user = userList.filter((item) => {
        return item.id === Number(userId)
    })[0]
    return HttpResponse.json(user);
  }),
];

リクエストボディの取得

await request.json()でformデータを取得します。今回は、bodyが空のレスポンスを返しています。

// 追記
type CreateUserFormData = Pick<User, "name">;


export const handlers = [
  ...,
  // ユーザー作成api
  http.post("/api/users", async ({ request }) => {
    const body = (await request.json()) as CreateUserFormData;
    const newUser: User = { id: userList.length + 1, name: body.name };
    userList.push(newUser);

    // 空のレスポンスを返却
    return new HttpResponse(null, {
      status: 201,
      statusText: "success",
    });
  }),
];

クエリパラメータの取得

await request.url()でurlを取得し、searchParams.get("パラメータkey名")で対象のクエリパラメータを取得します。

先ほど定義したGET /api/usersのモックAPIを、nameというクエリパラメータでfilteringするよう修正します。

export const handlers = [
  // ユーザー一覧取得api
  http.get("/api/users", ({ request }) => {
    const url = new URL(request.url);
    const userName = url.searchParams.get("name");

    if (!userName) {
      return HttpResponse.json(userList);
    }

    const filteredUserList = userList.filter((user) => {
      return user.name === userName;
    });

    return HttpResponse.json(filteredUserList);
  }),
  ...
]

?id=1&id=2&id=3のように、複数同じkeyが存在する場合は、

searchParams.get("id") -> searchParams.getAll("id")

とすることで、idの値を全て取得できます。

エラーハンドリング

nullのところに、エラー用のレスポンスボディを含めることもできます。

return new HttpResponse(null, {
  status: 404,
  statusText: "エラーメッセージ",
});

// or

return HttpResponse.json(null, {
  status: 404,
  statusText: "エラーメッセージ",
});

cookieの取得、送信

リクエストのcookieは、cookies.プロパティ名で取得できます。

レスポンスにcookieを含める場合、headerオプションに'Set-Cookie': 'key=value'内を追記します。

http.get("resource", async ({ cookies }) => {
  // cookieの値を取得
  console.log(cookies.mySecret)

  // cookieの送信
  return HttpResponse.json(null, {
    status: 201,
    statusText: "success",
    headers: {
      'Set-Cookie': 'mySecret=abc-123'
    }
  });
}),

まとめ

今回作成したhandlerは以下になります。

import { HttpResponse, http } from "msw";

type User = {
  id: number;
  name: string;
};

type CreateUserFormData = Pick<User, "name">;

const userList: User[] = [
  { id: 1, name: "テストユーザー1" },
  { id: 2, name: "テストユーザー2" },
  { id: 3, name: "テストユーザー3" },
  { id: 4, name: "テストユーザー4" },
];

export const handlers = [
  // ユーザー一覧取得api
  http.get("/api/users", ({ request }) => {
    const url = new URL(request.url);
    const userName = url.searchParams.get("name");

    if (!userName) {
      return HttpResponse.json(userList);
    }

    const filteredUserList = userList.filter((user) => {
      return user.name === userName;
    });

    return HttpResponse.json(filteredUserList);
  }),
  // 特定のユーザー取得api
  http.get("/api/users/:userId", ({ params }) => {
    const userId = Number(params.userId);

    if (!userId) {
      return HttpResponse.json(null, {
        status: 400,
        statusText: "bad request",
      });
    }
    const user = userList.filter((user) => {
      return user.id === Number(userId);
    })[0];

    if (!user) {
      return HttpResponse.json(null, {
        status: 404,
        statusText: "no resource",
      });
    }
    return HttpResponse.json(user);
  }),
  // ユーザー作成api
  http.post("/api/users", async ({ request }) => {
    const body = (await request.json()) as CreateUserFormData;
    const newUser: User = { id: userList.length + 1, name: body.name };
    userList.push(newUser);

    return HttpResponse.json(null, {
      status: 201,
      statusText: "success"
    });
  }),
  // cookie取得解説用Api(利用なし)
  http.get("resource", async ({ cookies }) => {
    console.log(cookies.mySecret);
    return HttpResponse.json(null, {
      status: 201,
      statusText: "success",
      headers: {
        "Set-Cookie": "mySecret=abc-123",
      },
    });
  }),
];

プロフィール


都内で約1年半Webエンジニアやってます!(2024/3現在)
業務ではフロントエンド, バックエンド, インフラ(AWS)まで担当してます。
利用言語:TS, JS, HTML, CSS, Python, C#
FW:Vue, React, FastAPI, Flask, Django, Streamlit
クラウド:AWS

タグ一覧

Ruff

alembic

Docker

pytest

Python

Poetry

i18n

SQLAlchemy

vscode

Nuxtr

Nodejs

Volta

MUI

Prettier

Eslint

Biome

Vite

swr

React

Netlify

daisyUI

Tailwind CSS

msw

microCMS

Nuxt

Vue

AWS