2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MSW(Mock Service Worker)でモックデータを永続化する

Posted at

はじめに

最近、MSWというモックサーバーがあることを知り、軽く触ってみたところ、使えそうだなと思ったのと同時にPOSTで登録したデータなど次回起動以降も使いまわせたら便利そうだなと思いました。

今回はMSWでデータを永続化する方法について紹介します。

前提

MSWについてはある程度の理解があることを前提とします。
下記あたりの記事をご覧いただければ、どんなことができるかわかると思います。

実装

永続化の実装としてはこちらのリポジトリsrc/test/server/db.tsから拝借します。
非常にコードがわかりやすいので特に解説はしないですが、一応、コメントだけつけています。
※簡略化のため、modelsは必要最低限としています。

db.ts
import { factory, primaryKey } from '@mswjs/data'

const models = {
  user: {
    id: primaryKey(Number),
    name: String,
    email: String,
  },
}

export const db = factory(models)

export type Model = keyof typeof db

// localStorageからデータ取得
export const loadDb = () =>
  Object.assign(JSON.parse(window.localStorage.getItem('msw-db') || '{}'))

// localStorageへデータ保存
export const persistDb = (model: Model) => {
  const data = loadDb()
  // @ts-ignore
  data[model] = db[model].getAll()
  window.localStorage.setItem('msw-db', JSON.stringify(data))
}

// モックDBの初期化
export const initializeDb = () => {
  const database = loadDb()
  // DBの復元
  Object.entries(db).forEach(([key, model]) => {
    const dataEntres = database[key]
    if (dataEntres) {
      dataEntres?.forEach((entry: Record<string, any>) => {
        model.create(entry)
      })
    }
  })
}

// localStorageをリセット
export const resetDb = () => {
  window.localStorage.clear()
}

initializeDb()

モック用API

データ登録するための処理を作ります。

users.ts
import { rest } from 'msw'

import { db, persistDb } from '@/mocks/db'

export const usersHandlers = [
  // getについては省略
  rest.post('/api/users', async (req, res, ctx) => {
    const data = await req.json()
    const id = db.user.count() + 1
    const result = db.user.create({
      ...data,
      id,
    })

    // データを追加したので保存(PATCHやDELETEも同様)
    persistDb('user')

    return res(
      ctx.status(200),
      ctx.json(result),
    )
  }),
]

ディレクトリ構造

最終的にMSWに関するディレクトリ構造はこのような感じになりました。

src
└── mocks
     ├── handlers
     │    └── index.ts
     │    └── users.ts
     ├── db.ts
     └── browser.ts

APIを叩いてみる

ここまで準備できたら、実際にモック用のAPIを叩いてみます。
MSWだからといって特に意識することなく、通常通りのコードを書けばOKです。

App.tsx
import { useEffect, useState } from 'react'
import { Button, Container, Flex, Input, List, MantineProvider } from '@mantine/core'
import { useForm } from '@mantine/form'

import { resetDb } from '@/mocks/db'

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

type Form = {
  name: string
  email: string
}

function App() {
  const [users, setUsers] = useState<User[]>([])
  const form = useForm({
    initialValues: {
      name: '',
      email: '',
    },
  })

  // user一覧取得
  const fetchData = async() => {
    const data = await fetch('/api/users').then((res) => res.json())
    setUsers(data)
  }

  // user情報登録
  const handleSubmit = async(values: Form) => {
    const data = await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify(values),
    }).then((res) => res.json())

    setUsers([...users, data])
    form.reset()
  }

  useEffect(() => {
    fetchData()
  }, [])

  return (
    <MantineProvider withGlobalStyles withNormalizeCSS>
      <Container>
        <form onSubmit={form.onSubmit(handleSubmit)}>
          <Flex my="md" gap={5}>
            <Input placeholder="UserName" {...form.getInputProps('name')} />
            <Input placeholder="Email" {...form.getInputProps('email')} />
            <Button type="submit">Send</Button>
            <Button color="red" onClick={() => { resetDb() }}>Reset</Button>
          </Flex>
        </form>

        {!users.length && (
          <p>user情報がありません</p>
        )}

        <List>
          {users.map(user => (
            <List.Item key={user.id}>
              {user.name}({user.email})
            </List.Item>
          ))}
        </List>
      </Container>
    </MantineProvider>
  )
}

export default App

データ追加

POSTすると、localStorageに値が追加されていることが確認できます。

msw01.gif

画面更新・再起動

ブラウザをリロードしたり、アプリケーションを再起動してもlocalStorageに保持されているデータを取得できていることが確認できます。

msw02.gif

注意事項

この記事を読んでいる読者層的には今更なことかもしれませんが、下記の点に注意が必要です。

  • localStorageは同じドメイン(今回ローカル環境なのでport番号も含む)で共有されるため、値を上書きされないよう注意
    • プロジェクトごとにkeyを変更する(今回の場合msw-dbになっています)
      • ただし、localStorageには容量制限があるため、同じドメインにデータを多くは保持できない
    • そもそもプロジェクトごとにport番号を変えてしまう
  • ブラウザを変えたら値は共有されない
    • どうしてもデータを引き継ぎたいのであれば、開発者ツールからコピペなどでデータを移しましょう

参考

最後に

GoQSystemでは一緒に働いてくれる仲間を募集中です!

ご興味がある方は以下リンクよりご確認ください。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?