mswjs / msw

Industry standard API mocking for JavaScript.
https://mswjs.io
MIT License
15.89k stars 517 forks source link

Support Next.js 13 (App Router) #1644

Closed kettanaito closed 1 week ago

kettanaito commented 1 year ago

Scope

Adds a new behavior

Compatibility

Feature description

As of 1.2.2, MSW does not support the newest addition to Next.js being the App directory in Next.js 13. I'd like to use this issue to track the implementation of this support since many people are currently blocked by this.

Prior investigation

I looked into this a month ago and discovered that there's difficulty to establish any process-wide logic in Next.js 13 due to the way Next itself is implemented internally. Very briefly, Next keeps two Node.js processes while it runs:

  1. One process is persistent (opened once on the same port and running there).
  2. Another process spawns at a random port, does its job, and gets killed.

This process separation is evident when you issue a hot reload to any of the layout components.

Reloading a root layout

When a hot update is issued through the root layout component, we can see the persistent Node.js process update. I don't remember any changes to the intermittent process.

Reloading a nested layout

When a hot update is issued for a nested layout, i.e. a certain page's layout, the intermittent process gets killed, then a new one spawns at its place (on a different port), likely evaluates the update, and keeps pending.

What's the problem with the two processes then?

In Node.js, MSW works by patching global Node modules, like http and https. Those patches must persist in the process in order for the mocking to work. Since Next uses this fluctuating process structure, it introduces two main issues:

  1. We cannot establish the global module patches once since Next's functionality is split across two different, unrelated processes. Usually, the module patching should be done somewhere in your root layout since it's conceptually guaranteed to be the highest hot update point no matter where you are in your application. That's not the case in Next.js, as the processes evaluating the root layout and individual page layouts are completely different, and the module patches in one process do not affect the other.
  2. Since the page-oriented (fluctuating) process constantly gets killed and spawned at random ports, I don't see a way to establish module patching in it at all to support API mocking for server-side requests issued in individual pages (or their layouts).

What happens next?

MSW is blocked by the way Next.js is implemented internally. I don't like this state of things but that's what we get—patching Node globals is not the most reliable of things and it will, inevitably, differ across frameworks depending on how those frameworks are implemented.

I would pretty much like for the Next.js team to assist us in this. There's very little we can do on the MSW's side to resolve this. In fact, I believe there's nothing we can do until there's a way to establish a module patch in Node.js for all Next.js processes at the same time.

If you're blocked by this, reach out to the Next.js team on Twitter or other platforms, letting them know this issue is important. I hope that would help the team prioritize it and help us resolve it together. Thanks.

kettanaito commented 1 year ago

To be entirely frank, nothing is compatible with Next.js 13 from patching-based API mocking solutions. With the way Next is orchestrated internally right now, to module patches will survive its bootstrapping. Just making this transparent so everybody's on the same page.

ganicus commented 1 year ago

This is 100% true with global fetch. I believe the intention is to make fetch ubiquitous so that it works identically in server and client components in a push to accommodate RSC design patterns.

Instead of surviving the bootstrapping would it be possible to patch after Next builds/initializes? I've seen workarounds that have used api wrappers that looked for env variables to switch msw mocks on or off.

I understand it's not ideal and that MSW use cases extend beyond just testing, but it would benefit a large portion of the community that is looking for a reliable test solution in the meantime.

kettanaito commented 1 year ago

Instead of surviving the bootstrapping would it be possible to patch after Next builds/initializes?

That's really a question to the Next.js team I'm raising. As a third-party tool, MSW cannot influence the way Next runs without Next explicitly giving some API to do that. Yes, that would be the best solution and I hope that's what happens.

The solution on the Next's side may be something like a new hook in the instrumentation.js module:

// instrumentation.js

// This is what Next currently supports.
// This function is called BEFORE Next bootstraps
// the environment (patches global fetch).
export function register() {
}

// This function is called as the very last thing
// when bootstrapping the environment. 
export function registerAfterEnv() {
  patchGlobals()
}

I'm a bit sad to see Next pursuing a testing integration instead of allowing third-parties to patch request-issuing modules. Testing is just one part of where one may use API mocking tools, and I dare say it's as big a part as prototyping and debugging.

I also hate to be this guy but this whole story would not exist if Next didn't meddle with globals and achieved user convenience through proper abstraction. MSW meddles with globals because there's no other choice. There is a choice for Next, but I trust the team to know better.

I've seen workarounds that have used api wrappers that looked for env variables to switch msw mocks on or off.

I believe those workarounds imply initializing setupServer deeper than your application's root. That's quite incorrect and may lead to all sorts of unexpected behavior. Module patching should sit on the root to survive hot updates and affect the entire server-side of the application as one would expect. Use those workarounds to your own risk.

SalahAdDin commented 1 year ago

This is 100% true with global fetch. I believe the intention is to make fetch ubiquitous so that it works identically in server and client components in a push to accommodate RSC design patterns.

Instead of surviving the bootstrapping would it be possible to patch after Next builds/initializes? I've seen workarounds that have used api wrappers that looked for env variables to switch msw mocks on or off.

I understand it's not ideal and that MSW use cases extend beyond just testing, but it would benefit a large portion of the community that is looking for a reliable test solution in the meantime.

Those workarounds work terribly.

kettanaito commented 1 year ago

Update

I've opened an official proposal to support side effects after the environment bootstrap in Next.js: https://github.com/vercel/next.js/discussions/56446

[!IMPORTANT] Please upvote that proposal to show that you need Next.js supporting MSW. Thank you.

hdsuperman commented 1 year ago

A temporary compromise solution:

// eslint-disable-next-line import/no-extraneous-dependencies
import { createServer } from '@mswjs/http-middleware';
import { handlers } from './handlers';

const httpServer = createServer(...handlers);
httpServer.listen(9090);
SalahAdDin commented 1 year ago

A temporary compromise solution:

// eslint-disable-next-line import/no-extraneous-dependencies
import { createServer } from '@mswjs/http-middleware';
import { handlers } from './handlers';

const httpServer = createServer(...handlers);
httpServer.listen(9090);

In which file do you this modification?

rmiller61 commented 1 year ago

A temporary compromise solution:

// eslint-disable-next-line import/no-extraneous-dependencies
import { createServer } from '@mswjs/http-middleware';
import { handlers } from './handlers';

const httpServer = createServer(...handlers);
httpServer.listen(9090);

do you have a complete working example of this? Curious if you're using the instrumentation hook discussed here in addition to the MSW middleware.

hdsuperman commented 1 year ago

code: @SalahAdDin @rmiller61

mocks/http.ts

import { createMiddleware } from '@mswjs/http-middleware';
import express from 'express';
import cors from 'cors';
import { handlers } from './handlers';

const app = express();
const port = 9090;

app.use(cors({ origin: 'http://localhost:3000', optionsSuccessStatus: 200, credentials: true }));
app.use(express.json());
app.use(createMiddleware(...handlers));
app.listen(port, () => console.log(`Mock server is running on port: ${port}`));

package.json

{
  "scripts": {
    "dev": "next dev",
    "mock": "npx tsx ./mocks/http.ts",
  }
}

axios settings:

axios.defaults.baseURL = 'http://localhost:9090';

start mock server yarn mock, start dev server yarn dev

SalahAdDin commented 1 year ago

code: @SalahAdDin @rmiller61

mocks/http.ts

import { createMiddleware } from '@mswjs/http-middleware';
import express from 'express';
import cors from 'cors';
import { handlers } from './handlers';

const app = express();
const port = 9090;

app.use(cors({ origin: 'http://localhost:3000', optionsSuccessStatus: 200, credentials: true }));
app.use(express.json());
app.use(createMiddleware(...handlers));
app.listen(port, () => console.log(`Mock server is running on port: ${port}`));

package.json

{
  "scripts": {
    "dev": "next dev",
    "mock": "npx tsx ./mocks/http.ts",
  }
}

axios settings:

axios.defaults.baseURL = 'http://localhost:9090';

start mock server yarn mock, start dev server yarn dev

So we start it as an independent server, cool! As a workaround looks great.

kettanaito commented 1 year ago

Thanks for proposing this, @hdsuperman. As a temporary solution, using MSW in a standalone server may work. But it limits the experience quite significantly, making you change the source code to request different resources and having no support during tests. I hope we move forward with the Next.js proposal in the upcoming weeks so we have a proper support for MSW in Next.

xereda commented 11 months ago

@louis-young We have msw working with the pages directory just fine. Make sure you have this code at the top of your pages component's index.tsx:

if (process.env.NEXT_PUBLIC_API_MOCKING === 'enabled') {
  // eslint-disable-next-line global-require
  require('@/mocks');
}

Could you present the structure of your mocks/index.[js/ts] file?

xereda commented 11 months ago

Thanks for proposing this, @hdsuperman. As a temporary solution, using MSW in a standalone server may work. But it limits the experience quite significantly, making you change the source code to request different resources and having no support during tests. I hope we move forward with the Next.js proposal in the upcoming weeks so we have a proper support for MSW in Next.

Firstly, congratulations on the excellent work leading the MSW library.

Do we have any news about support for Next.js?

rmiller61 commented 11 months ago

@xereda Artem followed up here. TL;DR it looks like Next is going to stop patching fetch and instead handle caching through its own cache/nostore primitives. So we're basically waiting for a stable release of those APIs in order to have MSW support Next's app router.

xereda commented 11 months ago

@xereda Artem followed up here. TL;DR it looks like Next is going to stop patching fetch and instead handle caching through its own cache/nostore primitives. So we're basically waiting for a stable release of those APIs in order to have MSW support Next's app router.

Thank you @rmiller61! I will follow this.

xereda commented 11 months ago

@xereda Artem followed up here. TL;DR it looks like Next is going to stop patching fetch and instead handle caching through its own cache/nostore primitives. So we're basically waiting for a stable release of those APIs in order to have MSW support Next's app router.

@rmiller61

In the example below, I don't use the App Router, but the error still occurs.

https://github.com/xereda/nextjs-msw-example/tree/main

hmacafee commented 11 months ago

@kettanaito any more update on this? I'd really love to use msw in my Next app (both for local dev and unit testing purposes), but I've been fighting with it for about a week. If not, what are you (or anyone else in this thread) using to mock api responses? I'd rather not resort to a barebones json-server but that's the best alternative I think I've found so far.

kettanaito commented 11 months ago

Hi, @hmacafee. No updates to far. As mentioned above, keep track of any changes in this discussion: https://github.com/vercel/next.js/discussions/56446.

Note that you can still use MSW client-side (in the browser) in Next.js. Nothing changed there. For server-side, we are waiting for Next.js to move away from patching (and restoring) fetch. Once they do, MSW should just work™.

Meanwhile, some folks are using @mswjs/http-middleware to move MSW request handlers to a standalone server (see https://github.com/mswjs/msw/issues/1644#issuecomment-1750722052). While this will work, note that this approach negates the main benefits of MSW so it's not preferred.

jviall commented 11 months ago

@kettanaito based on this issue #1801 it seems like even the client-side msw/browser portion of MSW is difficult to implement in Next. Even when doing all of the following:

I still hit a Module not found: Package path ./browser is not exported from package 'msw'.

I'm new to MSW and somewhat begrudgingly married to Next for the foreseeable future. If we ought to be able to work on the browser side even with https://github.com/vercel/next.js/discussions/56446 unresolved, could you point me in the right direction?

xereda commented 11 months ago

@kettanaito

I'm sorry, but I ended up getting lost with so much information. We believe in taking two steps back and reevaluating the problem.

What is the procedure for installing MSW in projects with Next.js 14? (And we can disregard the configuration via App Router)

philwolstenholme commented 11 months ago

@kettanaito

I'm sorry, but I ended up getting lost with so much information. We believe in taking two steps back and reevaluating the problem.

What is the procedure for installing MSW in projects with Next.js 14? (And we can disregard the configuration via App Router)

It is a big thread but that's because things are a bit complicated at the moment. Client side mocking can work but it may be a bit difficult to set up, React Server Component/server-side mocking won't work at all. This is caused by Next, not by MSW.

If you're already lost from reading this thread then wait till you also start reading the various issues/discussions on the Next.js GitHub project, plus Artem's Request for Comments too 😄

If you'd rather not dig into the details then the easiest thing is probably to wait for Vercel to make the changes that are necessary for MSW to work. Once that's happened then Vercel or MSW will be likely to make an example of using Next and MSW together that you can copy from.

This issue thread and https://github.com/vercel/next.js/discussions/56446 are the best places to watch for updates at the moment.

jd-carroll commented 10 months ago

Note: For anyone experiencing difficulty getting the wrong export from msw or @mswjs/interceptors (ie. browser vs node), take a look at my comment here. It may help with some of the issues experienced on this thread.

But also, I haven't done extensive browser testing with it, so I'd love to see if it holds up!

kettanaito commented 9 months ago

Regarding the cannot resolve ./browser import, I'm frankly at a loss as to why that happens. The only situation when it would, is when you import msw/browser in a Node.js process. I suspect when Next.js builds your client code, it doesn't respect the browser/node conditions, and the third parties still resolve using the node/default condition.

I believe folks have suggested how to fix this in next.config.js in https://github.com/mswjs/msw/issues/1877#issuecomment-1868467758.

kettanaito commented 9 months ago

Updates

I've shared my findings on the Next.js and MSW integration: https://twitter.com/kettanaito/status/1749496339556094316

Opened a pull request to add it as an example: https://github.com/mswjs/examples/pull/101

TL;DR

It's not there quite yet. The first step has been done but Next.js remains incompatible with MSW due to the lack of means to, once again, run an arbitrary async code once for both server-side and client-side parts of Next.js.

kettanaito commented 9 months ago

For anybody curious, I've got a somewhat functioning example of Next.js 14 + MSW right here: https://github.com/mswjs/examples/pull/101. If you read the tweet I posed above, you understand why it's not a full integration example I'm yet ready to recommend to everyone. But it can get you going. If you find something to help fix pending issues (see the pull request's comments), please let me know.

angusclarkdev commented 9 months ago

Hi @kettanaito – Just wanted to say that I love this package and have been using it for unit testing react components for the best part of three years. I'm currently working on a new project that uses Next 14 so have been eagerly following this discussion because it would be great to be able to use msw again.

In the mean time, I wondered if you had any suggestions for what the next best option might be for mocking network responses for unit tests? Our stack is vitest + react testing library (moved over from Cypress which is also having issues with Next 14) and we use react query for data fetching.

MrLoh commented 9 months ago

Playwright is supported to my knowledge and has build in network mocking. You might want to give that a try.

kettanaito commented 9 months ago

In the mean time, I wondered if you had any suggestions for what the next best option might be for mocking network responses for unit tests?

You don't run the entire app during unit testing, so you can choose tools like Vitest and React Testing Library + MSW to test individual units of your application. Highly recommend browsing through the Examples.

For E2E testing, you can use Playwright. Just bear in mind its mocking API wouldn't be able to intercept server-side requests your app makes (which makes sense; no E2E testing tools can do that). Once we land the end-to-end integration of Next + MSW, you'd be able to intercept and mock both server-side and client-side requests in E2E tests.

noreiller commented 9 months ago

On my side, I chose to implement a small mock server in combination with Playwright. I can run the tests in local and in the CI with a dedicated build. Technically, I redirected all my requests calls (server and browser) to a API route on my application and the mock server responds with the right mock.

kettanaito commented 9 months ago

@noreiller, a great strategy 👍 You can also use MSW as a standalone HTTP server. May be useful if you consider using it "natively" in Next.js but cannot as of now. Look into https://github.com/mswjs/http-middleware.

noreiller commented 9 months ago

@kettanaito Thanks. I took a look at https://github.com/mswjs/http-middleware before implementing it but I didn't want to have another server to handle. :stuck_out_tongue_closed_eyes:

SalahAdDin commented 9 months ago

@noreiller, a great strategy 👍 You can also use MSW as a standalone HTTP server. May be useful if you consider using it "natively" in Next.js but cannot as of now. Look into https://github.com/mswjs/http-middleware.

Great advise, thank you!

angusclarkdev commented 9 months ago

Thanks @kettanaito – I was under the impression that msw was having issues running in unit test environments because that is primarily what I've used the tool for, but of course, vitest / jest has no dependency on Next.js so I've given it a go this morning and it was really simple to set up.

SalahAdDin commented 8 months ago

Any news here?

nakjun12 commented 7 months ago

To summarize my findings after trying out various methods, it appears that mocking through Express seems to be the most effective approach. (link)

I modified an example provided by kettanaito and tested it. I preferred not to alter webpack, so I revised it to check for the presence of the window object. This approach allows us to update data upon server load, and upon navigating to a different page and calling the API again, the data updates can be observed through mocking. Similarly, mocking works well on the client-side as well.

However, it's important to note that the worker and server do not share memory. If data is updated at server mount, the client cannot detect it, and vice versa. Therefore, if you only want to test the CSR part in Next.js without modifying webpack, checking for the window object seems like a good approach.(component link)

Based on the current observations, creating an Express server and integrating it with msw for mocking appears to be the best solution.

VanTanev commented 7 months ago

Has anyone else tried next's experimental proxy?

https://github.com/vercel/next.js/tree/canary/packages/next/src/experimental/testmode/playwright

I was able to get RSC fetches AND suspense streaming to work correctly with MSW with some fanangling, and for frontend mocking, I used the approach from https://github.com/valendres/playwright-msw where the service worker is installed into the browser by playwright itself, and not by a nextjs integration.

With the above approach, I could share the same set of handlers for RSC and CSR. It only works with playwright, but it allows to fully mock a nextjs application. It does need some custom code, the code from playwright-msw cannot be used directly, but it seems like a good alternative.

SalahAdDin commented 7 months ago

Has anyone else tried next's experimental proxy?

https://github.com/vercel/next.js/tree/canary/packages/next/src/experimental/testmode/playwright

I was able to get RSC fetches AND suspense streaming to work correctly with MSW with some fanangling, and for frontend mocking, I used the approach from https://github.com/valendres/playwright-msw where the service worker is installed into the browser by playwright itself, and not by a nextjs integration.

With the above approach, I could share the same set of handlers for RSC and CSR. It only works with playwright, but it allows to fully mock a nextjs application. It does need some custom code, the code from playwright-msw cannot be used directly, but it seems like a good alternative.

Did you have any example about it?

VanTanev commented 7 months ago

Nothing complete, it was just me playing around. If I ever come back to it, I will try to make an example repo, or possibly try to submit a PR to playwright-msw.

SalahAdDin commented 7 months ago

Nothing complete, it was just me playing around. If I ever come back to it, I will try to make an example repo, or possibly try to submit a PR to playwright-msw.

We had to use MWS as a stand-alone http server.

mizdra commented 6 months ago

I have found a way to mock API requests without using msw/node. I share it in hopes that it will help people. It uses Route Handlers of App Router or API Routes of Pages Router.

This approach is very simple. First, define a handler that catches /api/* using Route Handlers or API Routes. Next, the request argument of the handler in Route Handlers or API Routes is processed by msw. This will cause msw to generate a response object. Finally, return the response object from the handler.

This approach does not conflict with monkey patching by Next.js for the fetch API because it does not use msw/node. In addition, because it does not use msw/browser, it avoids many of the problems caused by ServiceWorker.

chrisb2244 commented 6 months ago

@mizdra - how does that mock responses for your endpoints? Presumably you'd have to add/remove the real endpoints each time (Next, afaik, will only give you one response if you have such a setup - and I believe it prefers static routes to the catchall routes (i.e. the catchall is a fallback, not an override)).

It also seems like this would only work for routes defined in your application (e.g. in your example, myapp.com/api/my-endpoint), excluding the ability to fetch from e.g. https://jsonplaceholder.com/ or real external endpoints. Of course you could wrap all external calls with internal route handlers (and perhaps sometimes this is useful for other reasons), but it does limit your usecase if I understand correctly?

Edit: As I tried to play with the example, I also couldn't get the fetch call to reload correctly (I think perhaps this relates to Next.js' caching - but using the second argument to Next's fetch and then {cache: 'no-store'} or similar caused MSW to be unable to handle the request using your approach). If you update your handlers.ts file, does your page change?

giovanniruzzi commented 6 months ago

Hi there perhaps it's a trivial and it doesn't meet the requirements, but I managed to mock my v14 NextJS app using Axios instead of fetch. However, for production, the app will utilize fetch as recommended by Next.

mizdra commented 6 months ago

@chrisb2244 Your understanding is correct. My approach cannnot mock external API requests.

However, if you configure your project to request http://localhost:3000/api/* when you want to mock with msw and request the external API when you don't, you can mock the external API in a pseudo way. Please refer to the example below. You can toggle msw mocking with NEXT_PUBLIC_MSW=1 pnpm run dev or NEXT_PUBLIC_MSW=0 pnpm run dev.

As I tried to play with the example, I also couldn't get the fetch call to reload correctly (I think perhaps this relates to Next.js' caching - but using the second argument to Next's fetch and then {cache: 'no-store'} or similar caused MSW to be unable to handle the request using your approach). If you update your handlers.ts file, does your page change?

This is because I forgot to add export const fetchCache = "default-no-store"; to app/layout.tsx. I just added that and I think the problem has been resolved. When you edit handlers.ts, the content of the page should change as well.

how does that mock responses for your endpoints? Presumably you'd have to add/remove the real endpoints each time (Next, afaik, will only give you one response if you have such a setup - and I believe it prefers static routes to the catchall routes (i.e. the catchall is a fallback, not an override)).

I am sorry. I did not understand what you are saying. Could you please explain in more detail?

chrisb2244 commented 6 months ago

@mizdra - using your new example I can see the behaviour you describe.

This is because I forgot to add export const fetchCache = "default-no-store"; to app/layout.tsx. I just added that and I think the problem has been resolved. When you edit handlers.ts, the content of the page should change as well.

Now it reloads for me - I didn't know about this mechanism for setting a global default, thank you.

how does that mock responses for your endpoints? Presumably you'd have to add/remove the real endpoints each time (Next, afaik, will only give you one response if you have such a setup - and I believe it prefers static routes to the catchall routes (i.e. the catchall is a fallback, not an override)).

I am sorry. I did not understand what you are saying. Could you please explain in more detail?

I think you addressed this with the environment variable. I meant that it was not possible to have for example both /api/my-endpoint/route.ts and /api/[...slug]/route.ts (as you defined) and then have fetch('http://localhost:3000/api/my-endpoint') with variable mocking. The ${API_ENDPOINT} you introduce would allow this behaviour if required.

I think if you wanted to do this, you could also pass headers on your requests (for example with Playwright, or similar) and then parse those to determine handling in /api/[...slug]/route.ts, but I don't know the performance cost here. I also believe that your app will deploy with the test endpoints available, even if the ${API_ENDPOINT} is set to a different URL. This might be a problem, depending on the handling in the mocked endpoints (security, visibility, or just more deployed code). (You can directly navigate to www.my-deployed-site.com/api/my-mocking-user-endpoint even if the external endpoint is configured via ${API_ENDPOINT} to use www.a-secure-external-service.com/verify-user, for example).

mizdra commented 6 months ago

@chrisb2244

I meant that it was not possible to have for example both /api/my-endpoint/route.ts and /api/[...slug]/route.ts (as you defined) and then have fetch('http://localhost:3000/api/my-endpoint') with variable mocking. The ${API_ENDPOINT} you introduce would allow this behaviour if required.

That's right. You must take care to configure your API endpoints for mocking and any other API endpoints so that they do not conflict.

One of msw's philosophies is that you can mock without changing the application code at all. msw makes use of monkey patches and ServiceWorker to intercept requests in order to achieve this.

My approach avoids the problem of monkey patches in Next.js, but loses that philosophy. The user may have to rewrite the application code, such as https://github.com/mswjs/msw/issues/1644#issuecomment-2060722310, to mock the request. This is an important tradeoff.

mostafanadi commented 5 months ago

seems fetch on serverside is not working. finally i came up with a solution that think works. I created an interceptor for fetch and then inside that i put an if statement that says if env is development use axios otherwise use fetch. and inside your root layout you put :

if (process.env.NODE_ENV === "development") {
require("../mocks");
}

this is also my fetch interceptor :

export const fetchInstanse = async (
  input: string | URL | Request,
  init?: RequestInit | undefined
) => {
  let options = init;
  const session = await auth();
  const jwt = session?.user.accessToken;
  if (jwt) {
    options = {
      ...options,
      headers: { ...options?.headers, Authorization: `Bearer ${jwt}` }
    };
  }
  if (process.env.NODE_ENV == "development") {
    try {
      const res = await axios(
        input as string,
        options as AxiosRequestConfig<any>
      );
      return {
        json: () => res.data
      };
    } catch (error) {
      throw new Error("");
    }
  }
  const res = await fetch(input, options);
  if (res.ok) {
    const result = res;
    return result;
  } else {
    if (res.status == 401) {
      await signOut();
      return;
    }
    throw new Error(res.statusText);
  }
};

please ignore auth processes inside fetch instance. its specific for my application

SalahAdDin commented 5 months ago

seems fetch on serverside is not working. finally i came up with a solution that think works. I created an interceptor for fetch and then inside that i put an if statement that says if env is development use axios otherwise use fetch. and inside your root layout you put :

if (process.env.NODE_ENV === "development") {
require("../mocks");
}

this is also my fetch interceptor :

export const fetchInstanse = async (
  input: string | URL | Request,
  init?: RequestInit | undefined
) => {
  let options = init;
  const session = await auth();
  const jwt = session?.user.accessToken;
  if (jwt) {
    options = {
      ...options,
      headers: { ...options?.headers, Authorization: `Bearer ${jwt}` }
    };
  }
  if (process.env.NODE_ENV == "development") {
    try {
      const res = await axios(
        input as string,
        options as AxiosRequestConfig<any>
      );
      return {
        json: () => res.data
      };
    } catch (error) {
      throw new Error("");
    }
  }
  const res = await fetch(input, options);
  if (res.ok) {
    const result = res;
    return result;
  } else {
    if (res.status == 401) {
      await signOut();
      return;
    }
    throw new Error(res.statusText);
  }
};

please ignore auth processes inside fetch instance. its specific for my application

Shouldn't MSW intercept all those fetchings?

tompetk commented 3 months ago

This has worked for me:

// MswProvider.tsx
"use client";

import { handlers } from "@/utils/msw/handlers";
import { useEffect, useState } from "react";

export default function MswProvider({ children }: { children: React.ReactNode }) {
  const [mocksReady, setMocksReady] = useState(false);
  const mocksEnabled = process.env.NEXT_PUBLIC_API_MOCKING === "true";

  useEffect(() => {
    if (mocksEnabled) {
      if (typeof window !== "undefined") {
        import("msw/browser").then((a) => {
          a.setupWorker(...handlers)
            .start()
            .then(() => setMocksReady(true));
        });
      }
    }
  }, []);

  return (!mocksEnabled || mocksReady) && children;
}

With the usage from layout.tsx:

  return (
    <html lang={locale}>
      <body className={inter.className}>
        <MswProvider>
              {children}
        </MswProvider>
      </body>
    </html>
  );
ryota-murakami commented 3 months ago

@kettanaito I made next-app-router-with-msw-vitest-playwright based on the Draft Example.

Can I add a page for Next.js App Router Integration to the MSW documentation, or open a PR in the Next.js repo to create a template with MSW pre-configured using npx create-next-app --example with-msw my-app?

The reason is that there are currently no official resources on integrating the Next.js App Router with MSW, leading to incomplete setup projects and blog posts.

If you have time, I'd appreciate it if you could check if there are any improvements needed in my repo setup!

kettanaito commented 3 months ago

Update

I am collaborating closely with @feedthejim to conclude the Next.js + MSW usage example. He has shared some vital improvements to the server-side instantiation, which enabled proper support for HMR. There are still some issues pending around HMR and Next tearing down any previously existing patches (addressing in https://github.com/vercel/next.js/pull/68193), and also one client-side HMR issue.

This is where you stay up-to-date: https://github.com/mswjs/examples/pull/101

Please do not design custom interceptors/request handling logic. MSW already covers you, just be patient while we are finalizing the integration. Thanks.