Skip to content

defineMockData

Define Mock data with a memory-based sharing mechanism, used to share state between multiple Mock files.

Problem Background

Since the plugin compiles *.mock.* files as separate entries, this leads to inconsistent dependency relationships after compilation of mock files, with each mock file having its own independent scope. Even if multiple *.mock.* files import the same data.ts file, they are completely different data instances, so operations on data cannot be shared across different *.mock.* files.

defineMockData provides a memory-based data sharing mechanism to solve this problem.

Function Signature

ts
function defineMockData<T = any>(
  key: string,
  initialData: T
): MockData<T>

Parameters

key

  • Type: string
  • Description: Unique identifier key for Mock data
  • Required: Yes

initialData

  • Type: T
  • Description: Initial data value
  • Required: Yes

options

  • Type: { persistOnHMR?: boolean }
  • Description: An options object used to configure the data sharing mechanism.
  • Optional: Yes
  • Default Value: {}
    • persistOnHMR: Whether to preserve the initial data during HMR. The default value is false, meaning whether data will be reinitialized during HMR.

By default, the plugin internally determines whether data needs to be reinitialized by comparing whether the initial data has changed. However, when using libraries such as mockjs or faker, since the data is randomly generated, the plugin cannot directly determine whether the data has changed. Therefore, manual configuration of the persistOnHMR option is required.

Return Value

  • Type: MockData<T>
  • Description: Object containing getter, setter, and value properties

Return Value Structure

ts
type MockData<T> = readonly [
  () => T, // getter function
  (val: T | ((val: T) => T | void)) => void // setter function
] & {
  value: T // Directly accessible property
}

Examples

Basic Usage

data.ts
ts
import { defineMockData } from 'vite-plugin-mock-dev-server'

// Define shared user data
export const users = defineMockData('users', [
  { id: 1, name: 'John', email: 'john@example.com' },
  { id: 2, name: 'Jane', email: 'jane@example.com' }
])

// Define shared post data
export const posts = defineMockData('posts', [
  { id: 1, title: 'Hello World', authorId: 1 },
  { id: 2, title: 'Getting Started', authorId: 2 }
])
users.mock.ts
ts
import { defineMock } from 'vite-plugin-mock-dev-server'
import { users } from './data'

export default defineMock([
  // Get user list
  {
    url: '/api/users',
    method: 'GET',
    body: () => users.value
  },
  // Get single user
  {
    url: '/api/users/:id',
    method: 'GET',
    body: ({ params }) => {
      return users.value.find(u => u.id === Number(params.id))
    }
  },
  // Create user
  {
    url: '/api/users',
    method: 'POST',
    body: ({ body }) => {
      const newUser = {
        id: Date.now(),
        ...body
      }
      // Update data using setter
      users.value = [...users.value, newUser]
      return newUser
    }
  },
  // Delete user
  {
    url: '/api/users/:id',
    method: 'DELETE',
    body: ({ params }) => {
      const id = Number(params.id)
      // Update using setter function form
      users[1](prev => prev.filter(u => u.id !== id))
      return { success: true }
    }
  }
])

Using getter/setter Functions

counter.mock.ts
ts
import { defineMock, defineMockData } from 'vite-plugin-mock-dev-server'

const counter = defineMockData('counter', { count: 0 })

export default defineMock([
  {
    url: '/api/counter',
    method: 'GET',
    body: () => ({
      count: counter[0]().count // Using getter function
    })
  },
  {
    url: '/api/counter/increment',
    method: 'POST',
    body: () => {
      // Using setter function
      counter[1](prev => ({
        count: prev.count + 1
      }))
      return { count: counter[0]().count }
    }
  }
])

Complex Data Operations

todo.mock.ts
ts
import { defineMock, defineMockData } from 'vite-plugin-mock-dev-server'

interface Todo {
  id: number
  text: string
  completed: boolean
  createdAt: number
}

const todos = defineMockData<Todo[]>(
  'todos',
  [
    { id: 1, text: 'Learn Vite', completed: false, createdAt: Date.now() },
    { id: 2, text: 'Build Mock API', completed: true, createdAt: Date.now() }
  ],
  { persistOnHMR: true }, // Ensure data is not reset during HMR, preserving existing data state
)

export default defineMock([
  // Get todo list (supports filtering)
  {
    url: '/api/todos',
    method: 'GET',
    body: ({ query }) => {
      let result = todos.value

      // Filter by completion status
      if (query.completed !== undefined) {
        const isCompleted = query.completed === 'true'
        result = result.filter(t => t.completed === isCompleted)
      }

      // Search by keyword
      if (query.q) {
        result = result.filter(t =>
          t.text.toLowerCase().includes(String(query.q).toLowerCase())
        )
      }

      return result
    }
  },
  // Create todo
  {
    url: '/api/todos',
    method: 'POST',
    body: ({ body }) => {
      const newTodo: Todo = {
        id: Date.now(),
        text: body.text,
        completed: false,
        createdAt: Date.now()
      }
      todos.value = [newTodo, ...todos.value]
      return newTodo
    }
  },
  // Toggle completion status
  {
    url: '/api/todos/:id/toggle',
    method: 'PATCH',
    body: ({ params }) => {
      const id = Number(params.id)
      todos[1](prev =>
        prev.map(t =>
          t.id === id ? { ...t, completed: !t.completed } : t
        )
      )
      return todos.value.find(t => t.id === id)
    }
  },
  // Batch delete completed
  {
    url: '/api/todos/clear-completed',
    method: 'DELETE',
    body: () => {
      const completedCount = todos.value.filter(t => t.completed).length
      todos[1](prev => prev.filter(t => !t.completed))
      return { deleted: completedCount }
    }
  }
])

Hot Reload Handling

defineMockData has built-in hot reload handling mechanism:

  1. Caching Mechanism: Data is cached to ensure multiple Mock files access the same instance
  2. Hot Reload Protection: During hot updates, the initial data will not be reset if it remains unchanged (however, if using mockjs or faker-js to generate random data, you need to manually configure persistOnHMR to true).
  3. Data Persistence: Data state is preserved after file modification and recompilation

Important Notes

  1. Memory Storage: Data is only stored in memory and will be lost after service restart
  2. Persistence Recommendation: For persistent storage, it is recommended to use databases like lowdb, level, etc.
  3. Key Uniqueness: Ensure key is globally unique to avoid data conflicts
  4. Data Types: It is recommended to define TypeScript interfaces for complex data to get type hints
ts
// Persistence example (using lowdb)
import { Low } from 'lowdb'
import { JSONFile } from 'lowdb/node'
import { defineMock } from 'vite-plugin-mock-dev-server'

const adapter = new JSONFile('db.json')
const db = new Low(adapter, { posts: [] })
await db.read()

export default defineMock({
  url: '/api/posts',
  body: () => db.data.posts
})

Released under the MIT License