Vitest Cheatsheet

API Types & Mocks (Kubb, OpenAPI)

Use this Vitest reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

What is Kubb?

Kubb is a code generator that takes an OpenAPI specification and produces typed TypeScript clients, Zod schemas, React Query hooks, and MSW mock factories — eliminating the need to write boilerplate type definitions or mock data by hand.

Installation

npm install -D @kubb/cli @kubb/core @kubb/plugin-oas @kubb/plugin-ts @kubb/plugin-zod @kubb/plugin-client @kubb/plugin-msw

Kubb v3 (2024) renamed every @kubb/swagger-* package to @kubb/plugin-* (@kubb/swagger@kubb/plugin-oas). If you see @kubb/swagger-ts etc. in a config, it's a pre-v3 setup.

kubb.config.ts

import { defineConfig } from '@kubb/core'
import { pluginOas } from '@kubb/plugin-oas'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginClient } from '@kubb/plugin-client'
import { pluginMsw } from '@kubb/plugin-msw'

export default defineConfig({
  root: '.',
  input: {
    path: './openapi.yaml', // or a URL: 'https://api.example.com/openapi.json'
  },
  output: {
    path: './src/gen',
    clean: true,
  },
  plugins: [
    pluginOas({ validate: true }),

    // Generate TypeScript types
    pluginTs({ output: { path: 'types' } }),

    // Generate Zod schemas for runtime validation
    pluginZod({ output: { path: 'zod' } }),

    // Generate fetch/axios client functions
    pluginClient({
      output: { path: 'clients' },
      baseURL: 'https://api.example.com',
    }),

    // Generate MSW handlers from the spec
    pluginMsw({ output: { path: 'mocks' } }),
  ],
})

Running the Generator

npx kubb
# or add to package.json:
# "generate:api": "kubb"

Generated Output Structure

src/gen/
  types/
    User.ts            # TypeScript interfaces
    GetUsers200.ts
  zod/
    userSchema.ts      # Zod schemas
  clients/
    getUsers.ts        # fetch wrappers
    createUser.ts
  mocks/
    getUsersMock.ts    # MSW handlers
    createUserMock.ts

Using Generated Types

// src/gen/types/User.ts (generated)
export type User = {
  id: number
  name: string
  email: string
  role: 'admin' | 'viewer'
}

export type GetUsersResponse = User[]
// your code
import type { User } from '../gen/types/User'

function renderUser(user: User) {
  return `${user.name} <${user.email}>`
}

Using Generated Zod Schemas

// src/gen/zod/userSchema.ts (generated)
import { z } from 'zod'

export const userSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  role: z.enum(['admin', 'viewer']),
})

export type User = z.infer<typeof userSchema>
// runtime validation in tests
import { userSchema } from '../gen/zod/userSchema'

it('validates API response shape', async () => {
  const raw = await fetch('/api/users/1').then(r => r.json())
  const user = userSchema.parse(raw) // throws ZodError if invalid
  expect(user.email).toContain('@')
})

Using Generated MSW Handlers

// src/gen/mocks/getUsersMock.ts (generated)
import { http, HttpResponse } from 'msw'
import type { GetUsersResponse } from '../types/User'

export function getUsersMock(data?: Partial<GetUsersResponse>): HttpResponse {
  return HttpResponse.json([
    { id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' },
    ...(data ?? []),
  ])
}

export const getUsersHandler = http.get('/api/users', () => getUsersMock())
// src/mocks/handlers.ts
import { getUsersHandler, createUserHandler } from '../gen/mocks'

export const handlers = [getUsersHandler, createUserHandler]

Overriding Generated Mocks in Tests

import { server } from '../mocks/server'
import { http, HttpResponse } from 'msw'
import { getUsersMock } from '../gen/mocks/getUsersMock'

it('shows empty state', async () => {
  server.use(
    http.get('/api/users', () => HttpResponse.json([]))
  )
  // render and assert empty state...
})

it('shows user list', async () => {
  server.use(
    http.get('/api/users', () => getUsersMock([
      { id: 99, name: 'Bob', email: 'bob@test.com', role: 'viewer' },
    ]))
  )
  // render and assert user row...
})

Using Generated Client Functions

// src/gen/clients/getUsers.ts (generated)
import type { GetUsersResponse } from '../types/User'

export async function getUsers(): Promise<GetUsersResponse> {
  const res = await fetch('/api/users')
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.json()
}
// test using the generated client + MSW
import { getUsers } from '../gen/clients/getUsers'

it('fetches users', async () => {
  const users = await getUsers()
  expect(users).toHaveLength(1) // MSW handler returns 1 user
  expect(users[0].name).toBe('Alice')
})

Faker-Based Mock Data (with @kubb/plugin-faker)

npm install -D @kubb/plugin-faker @faker-js/faker
// kubb.config.ts
import { pluginFaker } from '@kubb/plugin-faker'

plugins: [
  pluginFaker({ output: { path: 'faker' } }),
]

Generated:

// src/gen/faker/createUser.ts
import { faker } from '@faker-js/faker'
import type { User } from '../types/User'

export function createUser(override: Partial<User> = {}): User {
  return {
    id: faker.number.int(),
    name: faker.person.fullName(),
    email: faker.internet.email(),
    role: faker.helpers.arrayElement(['admin', 'viewer']),
    ...override,
  }
}
import { createUser } from '../gen/faker/createUser'

it('works with seeded faker data', () => {
  const user = createUser({ role: 'admin' })
  expect(user.role).toBe('admin')
  expect(user.email).toContain('@')
})

openapi-ts Alternative

openapi-typescript is a simpler codegen for types only:

npm install -D openapi-typescript
npx openapi-typescript ./openapi.yaml -o ./src/gen/api.ts
// src/gen/api.ts (generated)
export interface paths {
  '/users/{id}': {
    get: {
      parameters: { path: { id: number } }
      responses: { 200: { content: { 'application/json': components['schemas']['User'] } } }
    }
  }
}

export interface components {
  schemas: {
    User: { id: number; name: string; email: string }
  }
}
// Type-safe fetch using openapi-fetch
import createClient from 'openapi-fetch'
import type { paths } from './gen/api'

const client = createClient<paths>({ baseUrl: 'https://api.example.com' })

const { data, error } = await client.GET('/users/{id}', {
  params: { path: { id: 1 } },
})
// data is typed as User

Testing Type-Safe Clients with MSW

import { server } from '../mocks/server'
import { http, HttpResponse } from 'msw'
import { client } from '../api/client' // openapi-fetch client

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

it('GET /users/:id returns typed user', async () => {
  server.use(
    http.get('https://api.example.com/users/1', () =>
      HttpResponse.json({ id: 1, name: 'Alice', email: 'a@a.com' })
    )
  )

  const { data } = await client.GET('/users/{id}', {
    params: { path: { id: 1 } },
  })

  expect(data?.name).toBe('Alice')
})

Key Differences: Kubb vs openapi-typescript

Kubbopenapi-typescript
TypesYesYes
Zod schemasYes (plugin)Via openapi-zod-client
Client functionsYes (fetch/axios)Via openapi-fetch
MSW handlersYes (plugin)Manual
Faker factoriesYes (plugin)Manual
React Query hooksYes (plugin)Manual
Config complexityHigherLow
Best forFull code gen pipelineTypes + lightweight client