Hi, lovely people! It's a pleasure to have you here again!
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.
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
After that, enter on the project and install the dependencies:
cd msw-example
yarn
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;
}
Let's create our types
on src/types/user/index.ts
:
export interface User {
id: string;
name: string;
userName: string;
avatar?: string;
email: string;
}
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);
}
}
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;
Now running the project, the result will be:
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
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
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));
}),
];
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);
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>
);
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();
}
}
Now, starting the app:
On console, we will notice that there is a message that msw is working and enabled:
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)
This is I need for testing msw. Thanks ad šš
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?