こんにちはこんばんわ!積ん読が増え続けている佐々木です! ここ最近フロントエンド開発時にモックをつかうことがあり、そこではモックにjson-serverを使っていました。 ですが、個人的にはあまり使い勝手が良くなかったので「もっと便利なのあるだろ〜」と思って調べてみたら「MSW(Mock Server Woker)」という結構イケてるライブラリがあったので実際に使ってみました! MSW(Mock Server Woker) MSWはService Workerを通じてAPIリクエストをインターセプトし、モックのレスポンスを返すことができるライブラリです。 ローカル開発以外にJestなどのテストやStoryBookにも利用することができます。 Introduction What is Mock Service Worker? mswjs.io json-serverとは違って、別プロセスを立てる必要がないというのが個人的な推しポイントです。 公式ドキュメントより、本番環境ではMSWの利用を勧めておりませんのでご留意ください。 環境 2022/8/27時点 MacBook Pro (2019late) Monterey v12.5 私はReact(TypeScript)でやってみましたので以下が主要な内容です。 node v18.2.0 react v18.2.0 typescript v4.7.4(後述のmswの兼ね合いで4.2.x <= 4.7.x で指定する必要がありました) MSWのinstall ではinstallから始めていきます(記事作成時、latestはv0.45.0でしたが念の為指定します) npm install msw@0.45.0 --save-dev install後にはmock用のディレクトリとhandlers.tsを作成しましょう。 mkdir src/mocks && touch touch src/mocks/handlers.ts handlers.tsの中身は以下のように記載します。 // src/mocks/handlers.ts import { graphql, GraphQLVariables } from 'msw'; export const handlers = [ graphql.query('GetUserInfo', (req, res, ctx) => { const authenticatedUser = sessionStorage.getItem('is-authenticated'); if (!authenticatedUser) { return res( ctx.status(400), ctx.errors([ { message: 'Not authenticated', errorType: 'AuthenticationError', }, ]) ); } return res( ctx.status(200), ctx.data({ user: { auth: authenticatedUser, firstname: 'USER_FIRSTNAME', lastname: 'USER_LASTNAME', }, }) ); }), graphql.mutation('Login', (req, res, ctx) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const username: string = req.variables.username; sessionStorage.setItem('is-authenticated', username); return res( ctx.status(201), ctx.data({ login: { username, }, }) ); }), ]; API定義 次はAPI定義を選択します。APIは「REST」と「GraphQL」から選択することができます。 今回は「GraphQL」で進めてみます。 GraphQLでのモックの場合は、GraphQLClientを設定する必要があるようです。 Since we will be working with a GraphQL API, we need to have a GraphQL client installed and configured in our application. We need that client primarily to dispatch queries and mutations. Please refer to the getting started steps of the respective client. https://mswjs.io/docs/getting-started/mocks/graphql-api#pre-requisites 私はApolloClientを使用することにします。 npm install @apollo/client graphql Get started with Apollo Client www.apollographql.com Service Worker -Browser- 次はブラウザでのリクエストをインターセプトさせるためのService Workerを作成します。 Service Workerは以下のコマンドを実行することで自動で作成されます。 なお、<PUBLIC_DIR>の部分は各JavaScriptプロジェクト毎にディレクトリを指定されていますので従いましょう。 ( ReactやVue.jsなどでは public , GatsbyJSでは static に読み替えてください。) npx msw init <PUBLIC_DIR> --save ex.react) npx msw init public/ --save コマンドを実行すると、指定したディレクトリ内に「mockServiceWorker.js」が作成されます。 Service Workerの構成を設定します。 touch src/mocks/browser.ts browser.tsには以下の内容を記載します。 // src/mocks/browser.ts import { setupWorker } from 'msw' import { handlers } from './handlers' export const worker = setupWorker(...handlers) 設定についてはこれで終了です。 画面の作成 簡単な画面を作成して正常に動作するか試していきます。 まずApolloClientの設定を記載します。 touch src/ApolloClient.ts // src/ApolloClient.ts import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' const cache = new InMemoryCache() const link = new HttpLink({ uri: 'http://localhost:3000/graphql', fetch: (...args) => fetch(...args), }) // Isolate Apollo client so it could be reused // in both application runtime and tests. export const client = new ApolloClient({ cache, link, }) 次に入力フォームを作成します。 touch src/LoginForm.tsx // src/LoginForm.tsx import React, { useCallback, useState } from 'react'; import { gql, useMutation, useQuery } from '@apollo/client'; const LOG_IN = gql` mutation Login($username: String!) { login(username: $username) { username } } `; interface LoginSessionDetails { id: number; username: string; } // login form export const LoginForm = () => { const [username, setUserName] = useState(''); // mutation const [Login, { data, loading, error }] = useMutation<{ Login: LoginSessionDetails }, { username: string }>(LOG_IN, { variables: { username }, }); const handleUsernameChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => { setUserName(event.target.value); }, []); const handleFormSubmit = useCallback( (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); Login({ variables: { username, }, }).catch((err) => console.log(err)); }, [username, Login] ); if (loading) { return <p>Loading...</p>; } if (error) { return <p>Error while fetching the user data ({error.message})</p>; } if (data) { return <UserInfo />; } return ( <div> <h1>Login</h1> <form onSubmit={handleFormSubmit}> <div> <label htmlFor='username'>Username:</label> <input id='username' name='username' value={username} onChange={handleUsernameChange} /> <button type='submit'>Submit</button> </div> </form> </div> ); }; interface UserInfo { firstname: string; lastname: string; } interface User { user: UserInfo; } const GET_USER_INFO = gql` query GetUserInfo { user { firstname lastname } } `; const UserInfo = () => { // queries const { loading, data, error } = useQuery<User>(GET_USER_INFO); if (loading) return <p>Loading ...</p>; return ( <> {data && ( <> <h3>FirstName:</h3> <div>{data.user.firstname}</div> <h3>LastName:</h3> <div>{data.user.lastname}</div> </> )} {error && ( <> <div>{error.message}</div> </> )} </> ); }; 次にindex.tsxを修正します。 // src/index.tsx import React from 'react'; import { ApolloProvider } from '@apollo/client'; import { client } from './ApolloClient'; import { LoginForm } from './LoginForm'; import { createRoot } from 'react-dom/client'; import { worker } from './mocks/browser'; const container = document.getElementById('root'); const root = createRoot(container!); // createRoot(container!) if you use TypeScript // Start the mocking conditionally. if (process.env.NODE_ENV === 'development') { worker.start().catch((err) => console.log(err)); } root.render( <React.StrictMode> <ApolloProvider client={client}> <LoginForm /> </ApolloProvider> </React.StrictMode> ); これで試してみる環境は整いましたので、ブラウザで確認していきます。 画面確認 http://localhost:3000/ にアクセスして検証ツールを開きます。 検証ツールのコンソールを開いて、以下のような表示がされていれば正常に動作しています。 現在の画面では、フォームに入力してSubmitボタンを押すと、mutation、queryを実行します。 入力してSubmitボタンを押すと、以下のような画面になることを確認出来ます。 ちなみになにも入力せずにSubmitボタンを押すとエラーを返すようにしています。 作成物のリンクはこちらです。 GitHub - daisuke8000/msw-sample Contribute to daisuke8000/msw-sample development by creating an account on GitHub. github.com 最後に 基本的に公式のGettingStartedに従った内容になりますが、すごいシンプルに実装できました。 皆さんのフロントエンド開発のモック検討時の選択肢にぜひ加えてみてください!! それではまた〜! The post フロントエンド開発のモックにMSWという選択肢 first appeared on miracleave Tech Blog .