Mocking API Calls in Storybook Using MSW On Your Sitecore XM Cloud Project

Simplify API testing in Storybook for Sitecore XM Cloud with MSW

November 23, 2024

By Sohrab Saboori

Mocking API Calls in Storybook for Next.js Sitecore XM Cloud

As a Sitecore developer, you often work with dynamic content served through REST APIs to fetch additional data or services. Testing these integrations in isolation can be challenging, especially when building or debugging components in Storybook. Thankfully, MSW (Mock Service Worker) and its Storybook addon make it easy to simulate API responses and streamline your development workflow.

Why Mocking is Important for Sitecore Development

Mocking REST API calls brings several critical benefits to Sitecore development:

  • Simulate Real-World Scenarios: Effortlessly mimic both successful and error responses without needing live backend services. This allows you to test various edge cases and ensure your components are resilient.
  • Isolated Component Testing: Decouple your components from the backend, enabling you to test them independently with consistent, predictable behavior.
  • Accelerated Development: Avoid delays caused by backend dependencies or unstable environments. Mocking lets you work seamlessly, even when the backend is unavailable or incomplete.
  • Enhanced Collaboration: Empower designers, developers, and testers to work in parallel by providing consistent mocked data for shared components and stories.

By integrating mocking into your Storybook setup, you can create a faster, more efficient, and reliable development workflow for your Sitecore projects.

Setting Up MSW in Storybook

To get started, install the required packages:

npm install msw msw-storybook-addon

Step 1: Initialize MSW in Storybook

Create or update your preview.tsx file in the .storybook folder to initialize MSW and configure it for Storybook:

import { initialize, mswDecorator } from 'msw-storybook-addon';
import '../src/styles/globals.css';

// Initialize MSW
initialize()

const preview = {
  parameters: {
    // your other code...
  },
  // Provide the MSW addon loader globally
  loaders: [mswLoader],
}
export default preview;

Step 2: Generate the Service Worker

For MSW to intercept requests in your Storybook environment, you'll need to generate a service worker and place it in your public folder:

npx msw init public/

This command will generate the necessary service worker files (mockServiceWorker.js). If your project doesn’t use a public folder (e.g., for some custom setups), refer to the MSW official guide for framework-specific paths.

Notes:

  • The public folder is the default static file directory in Next.js, making it compatible with MSW.
  • Ensure that your Storybook setup serves static files from the public directory for the service worker to function correctly.

Mocking REST API Calls in Storybook

In Sitecore development, it's common to interact with external APIs to fetch additional data. Let’s assume you have the following Next.js API route for fetching account details:

Step 1: Create an Example API Route

Example API Route (pages/api/accountDetails.ts)

import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';

type AccountDetailsResponse = {
  [key: string]: unknown;
};

type Data = {
  message?: string;
  data?: AccountDetailsResponse;
  error?: string;
};

export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
  try {
    if (req.method !== 'POST') {
      return res.status(405).json({ error: 'Method Not Allowed' });
    }

    const body = {
      messageHeader: { id: '3fa85f64-5717-4562-b3fc-2c963f66afa6' },
      selectedAccount: {
        id: 'db82eb2f-8977-47ad-93d1-121e0b9d7756',
        name: req.body.name,
        email: req.body.email,
        accountType: '01',
      },
    };

    const apiKey = process.env.SITECORE_API_KEY;
    if (!apiKey) throw new Error('API key is missing');

    const response = await axios.post<AccountDetailsResponse>(
      'https://endpoint.com/api/account-info',
      body,
      {
        headers: {
          'Content-Type': 'application/json',
          'api-key': apiKey,
        },
      }
    );

    res.status(200).json({ data: response.data });
  } catch (error) {
    console.error('API handler error:', error);
    res.status(500).json({ error: (error as Error).message || 'Internal Server Error' });
  }
}

Step 2: Fetch Data Using a Custom Hook

To fetch data from the above API route, use SWR (Stale-While-Revalidate). This hook simplifies data fetching and caching in your Next.js applications.

For more on SWR, see this comprehensive guide.

import useSWR from 'swr';

type AccountArgs = {
  name: string;
  email: string;
};

const fetcher = async (url: string, arg: AccountArgs) => {
  const response = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(arg),
  });

  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  return response.json();
};

export const useAccountDetails = (selectedAccount: AccountArgs) => {
  const { data, error, isValidating } = useSWR(['/api/accountDetails', selectedAccount], ([url, args]) => fetcher(url, args), {
    revalidateOnFocus: false,
  });

  return {
    accountDetails: data?.data,
    isAccountDetailsLoading: isValidating,
    accountDetailsError: error,
  };
};

Step 3: Mocking the API in Storybook

To simulate the API response in Storybook, we’ll use MSW. first define Mock Data

const accountInfoTestData = {
  data: {
    selectedAccount: {
      id: 'db82eb2f-8977-47ad-93d1-121e0b9d7756',
      name: 'Mocked User',
      email: '[email protected]',
      accountType: '01',
    },
  },
};

Add Mock Handlers

You can apply mocks either per story or globally:

1. Per Story Mocking

Use this method for testing isolated components with specific scenarios.

import { http } from 'msw';
import { Meta } from '@storybook/react';

export default {
  title: 'Components/AccountComponent',
  component: AccountComponent,
  parameters: {
    msw: {
      handlers: [
        http.post('/api/accountDetails', (req, res, ctx) => {
          return res(ctx.json(accountInfoTestData));
        }),
      ],
    },
  },
} as Meta;

2. Global Mocking in preview.tsx

Apply global handlers to make mocks available across all stories.

import { http } from 'msw';

export const parameters = {
  msw: {
    handlers: [
      http.post('/api/accountDetails', (req, res, ctx) => {
        return res(ctx.json(accountInfoTestData));
      }),
  //    Or just sample return
      //  http.post('/api/accountDetails', () => {
    //    return HttpResponse.json(accountInfoTestData);
    //  }),
    ],
  },
};

Step 4: Using the Hook in a Component

Here’s how you can utilize the useAccountDetails hook in your component:

const AccountComponent = ({ selectedAccount }: { selectedAccount: AccountArgs }) => {
  const { accountDetails, isAccountDetailsLoading, accountDetailsError } = useAccountDetails(selectedAccount);

  if (isAccountDetailsLoading) return <LoadingSpinner message="Loading account details..." />;
  if (accountDetailsError) return <div>Error: {accountDetailsError.message}</div>;

  return (
    <div>
      <h2>Account Name: {accountDetails?.selectedAccount?.name}</h2>
      <p>Email: {accountDetails?.selectedAccount?.email}</p>
    </div>
  );
};

Final Word on Mocking API Calls in Storybook for Next.js Sitecore XM Cloud Using MSW

Mocking REST API calls in Storybook using MSW provides Sitecore developers with a powerful toolset for isolating and testing components. By simulating real-world scenarios, you can develop and debug your Next.js components without relying on live backend services. This not only speeds up the development process but also ensures your components behave predictably under various conditions, such as successful API responses, errors, or edge cases.

Incorporating mocking into your Storybook workflow offers the following key benefits:

  • Increased Productivity: No waiting for backend services or dealing with unstable environments.
  • Improved Component Reliability: Ensure components handle API responses gracefully, even in failure scenarios.
  • Simplified Collaboration: Mocked data allows designers, developers, and testers to work in parallel without dependency on live APIs.

By following the steps outlined in this blog, you’ll have a solid foundation for mocking REST APIs in your Sitecore projects, making your development workflow smoother and more efficient.

References

Photo of Fishtank employee Sohrab Saboori

Sohrab Saboori

Senior Full-Stack Developer

Sohrab is a Senior Front-End Developer with extensive experience in React, Next.js, JavaScript, and TypeScript. Sohrab is committed to delivering outstanding digital solutions that not only meet but exceed clients' expectations. His expertise in building scalable and efficient web applications, responsive websites, and e-commerce platforms is unparalleled. Sohrab has a keen eye for detail and a passion for creating seamless user experiences. He is a problem-solver at heart and enjoys working with clients to find innovative solutions to their digital needs. When he's not coding, you can find him lifting weights at the gym, pounding the pavement on the run, exploring the great outdoors, or trying new restaurants and cuisines. Sohrab believes in a healthy and balanced lifestyle and finds that these activities help fuel his creativity and problem-solving skills.