DEV Community

Cover image for Mocking Your API's Calls Using Mocking Service Worker (MSW)
Kevin Toshihiro Uehara
Kevin Toshihiro Uehara

Posted on

Mocking Your API's Calls Using Mocking Service Worker (MSW)

Hi, lovely people! It's a pleasure to have you here again!

Patrick Heart Image Meme

Today, I'm gonna talk about a library called Mocking Service Worker or better know as MSW.

Sometimes, when you are working in some feature, both backend and frontend team start to developing together. Or maybe the frontend team, start the development of the feature first and the API's sometimes are not ready yet. Or another situation, you start your frontend project first, and only after that will start the development of the backend and the API's.

But the fact is, you don't have the API's or your endpoins ready yet. So what you do?

Probably your answer it will be: "Well... I'll mock the response of my API". And yes, commonly we just mock the response that we expect.

But in ALL cases we will need to refact the code to really integrate with the API and make sure that our code is callling the endpoint with the right contract that was defined.

Also, we need to add or update the tests to ensure that a call to the API was made with the correct data.

BUT what if we could make a call to a fake API and we don't need to worry with refact all the code after with the real integration or add or update our tests. This will make the frontent development of the feature more dynamic.

When the API is ready we just change the url and everything will be working. But until there, we will use a fake API to mock the calls.

So in this case, I introduce to you the MSW.
MSW is a library where we can add in our project, and a Service Worker will be responsible to intercept the calls and respond to our client as a "fack backend" response.

Mock by intercepting requests on the network level. Seamlessly reuse the same mock definition for testing, development, and debugging. MSW, also can work with REST API and GraphQL.

Diagram how the MSW works

So now that you are familiar with MSW concepts let's apply an example, creating a React project. First we will simulate and call the API without MSW and after that we will integrate it into the project.

Creating the project

I will be using the Vite to create our react boilerplate using the command:

yarn create vite msw-example --template react-ts
Enter fullscreen mode Exit fullscreen mode

After that, enter on the project and install the dependencies:

cd msw-example
yarn
Enter fullscreen mode Exit fullscreen mode

I will remove the App.css and in our index.css add:

body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: Arial, Helvetica, sans-serif;
}

.container {
  display: flex;
  flex-direction: column;
}

.card {
  width: 50%;
  display: flex;
  flex-direction: column;
  margin: 3px;
  border: 1px solid #000;
}

button {
  width: 200px;
  background-color: #1e40af;
  color: #fff;
  font-size: medium;
  padding: 15px;
  border-radius: 4px;
}

button:hover {
  background-color: #2563eb;
  cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode

Let's create our types on src/types/user/index.ts:

export interface User {
  id: string;
  name: string;
  userName: string;
  avatar?: string;
  email: string;
}
Enter fullscreen mode Exit fullscreen mode

Now create our service on src/services/user/index.ts that will be responsible to call the API's.

import { User } from "../../types/user";

export class UserService {
  static async getUsers(): Promise<User[]> {
    // Fetch User API
    const mockUsers = new Array(3).fill(null).map((_, index) => ({
      id: index.toString(),
      email: "kevin@gmail.com",
      name: "kevin",
      userName: "uehara.kevin",
      avatar:
        "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/54.png",
    }));

    return Promise.resolve(mockUsers);
  }
}
Enter fullscreen mode Exit fullscreen mode

Notice that I'm mocking the users on code. I'm creating an array with 3 users with the same data and returning as a promise.

Now in our App.tsx, let's udpate the code with:

import { useState } from "react";
import { User } from "./types/user";
import { UserService } from "./services/user";

function App() {
  const [users, setUsers] = useState<User[]>([]);

  const handleFetchUsers = async () => {
    const users = await UserService.getUsers();
    setUsers(users);
  };

  return (
    <div className="container">
      <h1>Mocking Service Worker Example</h1>
      <button onClick={handleFetchUsers}>Fetch Data</button>
      {users.map((user) => (
        <div className="card" key={user.id}>
          <img src={user.avatar} width="100px" height="100px" />
          <label>Name: {user.name}</label>
          <label>UserName: {user.userName}</label>
          <label>Email: {user.email}</label>
        </div>
      ))}
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now running the project, the result will be:

Gif Application running without msw

We are just mocking the response in our code and any request is made.

Now Let's play with MSW.

First let's add as development dependency:

yarn add -D msw @faker-js/faker 
Enter fullscreen mode Exit fullscreen mode

We are adding two dependencies, the MSW and the faker (not necessary) but it will generate dynamic fake data for our project.

Let's create the MSW Service worker, that will be responsible to intercept the calls and return on network level. (the public/ can change if you are using some frontend tool, for example I'm using vite. If you are using Angular, for example is diffent. For this read the docs)

npx msw init public/ --save
Enter fullscreen mode Exit fullscreen mode

This command will create a file service worker on your /public folder called mockServiceWorker.js.

Now, let's create the handlers, that we will map the endpoints of our API. So create a folder called src/mocks, and inside this folder, create a file called handler.ts with this code:

import { rest } from "msw";
import { faker } from "@faker-js/faker";

export const handlers = [
  rest.get("/users", (_, res, ctx) => {
    const mockUsers = new Array(3).fill(null).map(() => ({
      id: faker.number.int(),
      userName: faker.internet.userName(),
      name: faker.person.fullName(),
      email: faker.internet.email(),
      avatar: faker.image.avatar(),
    }));

    return res(ctx.status(200), ctx.json(mockUsers));
  }),
];
Enter fullscreen mode Exit fullscreen mode

Notice that the logic of create the mock, the we created on our Service, now it's in our handler mock API. And now we are using the faker to generate dynamic data.

Now in the same folder, we will create the file called browser.ts, where we will create the setup for our worker:

import { setupWorker } from "msw";
import { handlers } from "./handler";

export const worker = setupWorker(...handlers);
Enter fullscreen mode Exit fullscreen mode

Notice, that we can create a list of handlers for each mock of API if we need and pass on setupWorker.

Let's change the file main.tsx to run the MSW ONLY in development mode:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";

if (import.meta.env.DEV) {
  const { worker } = await import("./mocks/browser.ts");
  worker.start();
}

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

So we just want to start the MSW on development mode, because on production we will be using the real API integrated.

To finish, just let's update our service on src/services/user/index.ts:

import { User } from "../../types/user";

export class UserService {
  static async getUsers(): Promise<User[]> {
    const users = await fetch("/users");
    return users.json();
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, starting the app:

On console, we will notice that there is a message that msw is working and enabled:

Console Output that the MSW is enabled

Gif of our application running with MSW

And that's it! Now our service worker is intercepting all calls of our API (thinking that is not done yet) and we can integrate, respect the contract API and create our tests to validate the calling of our API.

MSW works as a fake backend, but will streamline frontend development without having to rely on API development or mocking data in code.

That's it folks!

Thank you so much!

Contacts:

Linkedin: https://www.linkedin.com/in/kevin-uehara/
Instagram: https://www.instagram.com/uehara_kevin/
Twitter: https://twitter.com/ueharaDev
Github: https://github.com/kevinuehara
Youtube Channel: https://www.youtube.com/channel/UC6VSwt_f9yCdvEd944aQj1Q

Top comments (2)

Collapse
 
huynhtruong01 profile image
huynhtruong01 • Edited

This is I need for testing msw. Thanks ad 😊😊

Collapse
 
samirfcis profile image
Samir

wouldn't it be easier if I used an online api mocking tool like mock-api.net to make the creation of the mocked api's easier?