Jul 9, 2024

Next.js + Storybook with Auto‑generated API Mocks (TypeSpec, Orval, SWR, MSW)

In the previous post we generated OpenAPI with TypeSpec and SWR hooks with Orval.

Here we’ll use Orval’s MSW (Mock Service Worker) generation to make Storybook request mocked APIs.

What is MSW?

MSW is a mocking library for web apps. It intercepts requests (via Service Worker in the browser) and returns mock responses.

Compared to alternatives:

  • Simpler than replacing calls in code; no extra mock plumbing in your code.
  • Simpler setup than running a separate JSON Server mock process.

MSW also works server‑side (Node.js) by intercepting the http module.

Storybook

Storybook lets you develop UI components in isolation.

With Next.js 13, MSW integration with a running app can be tricky—according to this issue, it didn’t work with either Pages or App Router:

This project develops components in Storybook, which is sufficient; below is how to use MSW from Storybook.

Add to Storybook

Use the MSW addon:

MSW Storybook Addon

Install:

npm i msw msw-storybook-addon -D

Create the service worker

Generate the worker script under public/:

npx msw init public/

Update .storybook/preview.ts

Add the addon and pass handlers (created below):

import { initialize, mswLoader } from "msw-storybook-addon";
import { handlers } from "../mocks/handlers";

// Initialize MSW
initialize();

const preview: Preview = {
  parameters: {
    msw: {
      handlers
    }
  },
  loaders: [mswLoader]
};

Generate MSW mocks with Orval

Edit orval.config.ts

Add mock: true so Orval generates mocks along with SWR files:

module.exports = {
  "user-api": {
    input: {
      target: "./openapi.yml",
    },
    output: {
      mode: "split",
      target: "./api/endpoints",
      schemas: './api/models',
      client: "swr",
      mock: true,
    },
    hooks: {
      afterAllFilesWrite: "prettier --write",
    },
  },
};

Generated userAPI.msw.ts

After enabling mocks, run npx orval. You’ll get userAPI.msw.ts with MSW handlers and mock data generation using @faker-js/faker.

Add mocks/handlers.ts

Create mocks and export handlers:

mkdir mocks
cd mocks
import { getUserAPIMock } from "../api/userAPI.msw.ts";

export const handlers = getUserAPIMock();

These handlers are referenced in .storybook/preview.ts. Use the generated SWR hooks in components as usual and MSW will serve responses:

const { data, error, isLoading } = useUsersGetUsers();

Customize mock data

Customize fields via the properties option:

Example to randomize name/email/image URL:

module.exports = {
  "user-api": {
    input: {
      target: "./openapi.yml",
    },
    output: {
      mode: "split",
      target: "./api/endpoints",
      schemas: './api/models',
      client: "swr",
      mock: true,
      override: {
        mock: {
          properties: {
            name: () => faker.name.fullName(),
            email: () => faker.internet.email(),
            '/image/': () => faker.image.url(),
          },
        }
      },
      hooks: {
        afterAllFilesWrite: "prettier --write",
      },
    },
  },
};

Summary

Development flow:

  1. Define the API in TypeSpec (.tsp).
  2. Generate OpenAPI YAML.
  3. Use Orval to generate SWR hooks and MSW mocks.
  4. Develop components in Storybook against the MSW API.