如何将 msw 与 Nextjs 13.2.1 一起使用(错误:无法访问服务器上的worker.start。)

Mah*_*dra 5 mocking typescript reactjs next.js msw

我正在集成一个模拟 API,它基本上发送一个响应对象,其中包含一组应该显示在 UI(聊天框)中的消息以及用户名、用户图片等。

由于重大更改,我在设置msw 1.1.0with Next JS 13.2.1 (带有布局的实验应用程序目录)时遇到了一些麻烦。然而这是我的代码。

到目前为止我的代码是这样设置的

src/app/(用户)/live-view/page.tsx

import React from "react";
import ThreadPostList from "../../components/threadList";
 import { worker } from '../../../mocks/browser';

if (process.env.NODE_ENV === 'development') {
  worker.start();
}

async function getMessages() {
  const response = await fetch("http://localhost:3000/api/messages");
  // const data = await response.json();
  return response;
}


async function LiveViewPage() {
  const messages = await getMessages();
  const convertedMessages = Object.keys(messages);
  // console.log(convertedMessages, "ConvertedMessages");
  
  Object.values(messages).forEach(val => console.log(val));

  
  return (
    <div className="border-t border-gray-200 py-4 divide-y divede-gray-200">
      <ThreadPostList />

    
     
    </div>
  );
}

export default LiveViewPage;
Run Code Online (Sandbox Code Playgroud)

我的 Msw 模拟 API 位于

src/mocks/handler.ts

import { rest } from "msw";
import { UserWithMessages } from "./types";

const mockData: UserWithMessages[] = [
  {
    user: {
      id: "user1",
      name: "John Doe",
      profilePic: "https://example.com/user1.jpg",
    },
    messages: [
      {
        id: "msg1",
        senderId: "user1",
        text: "Hey, how are you?",
        createdAt: new Date("2022-01-01T12:00:00Z"),
      },
      {
        id: "msg2",
        senderId: "user1",
        text: "Did you get my email?",
        createdAt: new Date("2022-01-02T12:00:00Z"),
      },
    ],
  },
  {
    user: {
      id: "admin",
      name: "Admin",
      profilePic: "https://example.com/admin.jpg",
    },
    messages: [],
  },
];


export const handlers = [
  rest.get<UserWithMessages[]>("/api/messages", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(mockData));
  }),
];

Run Code Online (Sandbox Code Playgroud)

最后我的 browser.ts 位于

src/mocks/browser.ts

"use client"

import { setupWorker } from 'msw';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);
Run Code Online (Sandbox Code Playgroud)

当我运行服务器时它抛出错误

错误:无法访问服务器上的worker.start。您不能从服务器组件点入客户端模块。您只能传递导入的名称。

我在哪里以及如何启动我的工作人员?我是否必须在模拟下创建一个文件并在那里调用服务器并在我的 page.tsx 中导入该组件?任何帮助将不胜感激,谢谢

Jas*_*oss 3

自从 Mock Service Worker 进入稳定版本以来,我一直在努力让 Mock Service Worker 在应用程序目录结构中与 NextJS 一致运行。我已经取得了一定程度的成功,但我怀疑这不是理想的解决方案。我们仍在等待 NextJS 团队的合法示例实现。

由于您看到的确切错误,我们无法调用服务器渲染组件上的工作程序。相反,我已将 fetch 调用转移到一个共享服务中,该服务只是打字稿:

./services/api-service.ts

async function initMocks() {
  if (typeof window === 'undefined') {
    const { server } = await import('./src/mocks/server')
    await server.listen({ onUnhandledRequest: 'bypass' })
  } else {
    const { worker } = await import('./src/mocks/browser')
    await worker.start({ onUnhandledRequest: 'bypass' })
  }
}

// Note the change in ENV var name here
// https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser
if (process.env.NEXT_PUBLIC_MOCK_APIS === 'enabled') {
  initMocks()
}

export const makeApiRequest = async (path: string) => {
  try {
    const resp = await fetch(path)
    if (resp.ok) {
      const result = await resp.json()
      return result
    }
  } catch (err: any) {
    // handle errors
  }
}
Run Code Online (Sandbox Code Playgroud)

我不知道为什么,但是在像这样的较低级别实现 MSW,并且在实际的 React 组件之外对我来说比尝试在页面或布局级别初始化它更成功,因为它是在 NextJS 页面目录中实现的例子

这会将您的页面组件更改为顶部看起来更像这样:

src/app/(用户)/live-view/page.tsx

import React from "react";
import ThreadPostList from "../../components/threadList";
import { makeApiRequest} from './services/api-service';

async function getMessages() {
  const response = await makeApiRequest("http://localhost:3000/api/messages");
  return response;
}
Run Code Online (Sandbox Code Playgroud)

我在这里注意到的另一件事是,您的处理程序正在捕获相对路径,这也可能在服务器端出错。服务器处理程序缺少根 URL 的上下文,并且我通过其中的完整路径或路径匹配取得了更好的成功。所以要么:

export const handlers = [
  rest.get<UserWithMessages[]>("http://localhost:3000/api/messages", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(mockData));
  }),
];
Run Code Online (Sandbox Code Playgroud)

或者:

export const handlers = [
  rest.get<UserWithMessages[]>("*/api/messages", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(mockData));
  }),
];
Run Code Online (Sandbox Code Playgroud)

如果您想使用 MSW 运行 Jest 测试或在已部署环境或计算机以外的其他地方运行某些测试,后者可能会派上用场。

从技术上讲,在您的问题以及任何遵循 NextJS 文档中概述的最佳实践的应用程序中,我们根本不需要 MSW 的浏览器实现,因为所有数据都将在服务器组件中获取并用于补充客户端。但是,我将其留在这里是为了与以前的实现进行比较时提供一些一致性。

在我写这篇文章时,我正在使用 msw@1.2.1 和 next@13.4.3