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.