Back

Mocking API servers with Mock Service Worker - MSW

Mocking API servers with Mock Service Worker - MSW

A mock API server is an imitation or clone of a real back-end server that mimics a real server by intercepting requests and providing realistic responses.

Mock API servers are helpful during development and testing when a live server is unavailable or does not need to be polluted with test data.

Mock Service Worker (MSW) is an API mocking library that uses the Service Worker API to intercept requests. This tutorial will teach you how to create a mock API server for your applications using MSW.

Setting up Your Development Environment

To get started with MSW, install it in your project’s root directory by running the command below:

npm install msw

Next, create a mocks folder in your src folder by running the command below:

mkdir mocks

This folder will contain all the files related to the mock API server.

Finally, create a handler.js and a browser.js file in your mocks folder. The handler.js file will contain all the route handler logic for the application. The browser.js file is where you will configure and start your service worker for request interceptions.

Defining REST Mocks

Since you’re mocking a REST API, in your handler.js file, import rest from msw.

Like so:

//handler.js
import { rest } from "msw";

rest is a namespace exposed by the MSW library that contains essential methods for mocking a REST API, such as get and post, which you can use to handle HTTP requests like GET and POST.

Next, create and export an array, handlers.

Like so:

//handler.js
export const handlers = [];

This array will contain all the mock route handlers for your application. In this tutorial, you will create, Create, Read, Patch, and Delete (CRUD) mock endpoints for your application.

Next, add the following code to your handler.js file:

const blogs = [
    {
      "id": 1,
      "title": "sunt aut facere",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum"
    },
    {
      "id": 2,
      "title": "qui est esse",
      "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel"
    },
    {
      "id": 3,
      "title": "ea molestias quasi",
      "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur"
    }
];

The code block above will serve as a database for your mock API server. Note that your test data should implement the same schema your real API server will implement to ensure that your mock server produces more realistic results.

Next, create a mock route handler for a GET request to api/blogs by adding the code block below to your handlers array. This endpoint should return all the blogs:

rest.get("/api/blogs", (req, res, ctx) => {
    return res(ctx.json(blogs));
  }),

We called the get method on rest in the code block above. This method takes two arguments, a path and a response resolver. A response resolver is a callback function that contains all the logic required to resolve a request. It takes three arguments, request (req), response (res), and context (ctx). request contains all the details about the current request. response is used to construct the mock response. context contains request-specific utility functions that help build the mock response. Then we return the blogs array as JSON to the client by turning it into JSON using the ctx.json method and wrapping it in res.

Next, create a mock route handler for a GET request to api/blogs/:id by adding the code block below to your handlers array:

rest.get("/api/blogs/:id", (req, res, ctx) => {
    //Extracting id from URL
    const id = req.params.id;
    //Getting blog from db (array)
    const blog = blogs.find((blog) => blog.id === id);

    //Sending response
    if (blog) {
      return res(ctx.json(blog));
    } else {
      return res(ctx.json({ message: "Blog not found" }));
    }
  }),

This endpoint should return a single blog post.

Next, create a mock route handler for a POST request to api/blogs/new by adding the code block below to your handlers array.

rest.post("/api/blogs/new", async (req, res, ctx) => {
    //Getting request body in json
    const blog = await req.json();

    //Assigning ID
    blog.id = blogs.length + 1;


    //Adding blog to db (array)
    blogs.push(blog);

    //Sending response and setting status code
    return res(
      ctx.status(201),
      ctx.json({
        id: blogs.length + 1,
        blog,
      })
    );
  }),

This endpoint should return and add a new blog to the existing list.

Next, create a mock route handler for a DELETE request to api/blogs/:id by adding the code block below to your handlers array:

rest.delete("api/blogs/:id", (req, res, ctx) => {
    const id = req.params.id;
    const blogIndex = blogs.findIndex((blog) => blog.id === id);

    //Removing blog from db (array)
    blogs.splice(blogIndex, 1);

    return res(ctx.json({ message: "Deleted successfully" }));
  }),

This endpoint should delete the specified blog post.

Next, create a mock route handler for a PATCH request to api/blogs/:id by adding the code block below to your handlers array:

rest.patch("api/blogs/:id", async (req, res, ctx) => {
    const id = req.params.id;
    const reqBody = await req.json();
    const blog = blogs.find((blog) => blog.id === id);

    //Updating blog
    blog.title = reqBody.title;
    blog.body = reqBody.body;

    //Sending response
    return res(ctx.json({ message: "Updated successfully" }));
  }),

This endpoint should edit the specified blog post.

Your finished handlers array should look like the code block below:

export const handlers = [
  rest.get("/api/blogs", (req, res, ctx) => {
    return res(ctx.json(blogs));
  }),

  rest.get("/api/blogs/:id", (req, res, ctx) => {
    //Extracting id from URL
    const id = req.params.id;
    //Getting blog from db (array)
    const blog = blogs.find((blog) => blog.id === id);

    //Sending response
    if (blog) {
      return res(ctx.json(blog));
    } else {
      return res(ctx.json({ message: "Blog not found" }));
    }
  }),

  rest.post("/api/blogs/new", async (req, res, ctx) => {
    //Getting request body in json
    const blog = await req.json();
    //Creating unique id
    currentId += 1;

    //Assigning ID
    blog.id = currentId;

    //Adding blog to db (array)
    blogs.push(blog);

    //Sending response and setting status code
    return res(
      ctx.status(201),
      ctx.json({
        id: currentId,
        blog,
      })
    );
  }),

  rest.delete("api/blogs/:id", (req, res, ctx) => {
    const id = req.params.id;
    const blogIndex = blogs.findIndex((blog) => blog.id === id);

    //Removing blog from db (array)
    blogs.splice(blogIndex, 1);

    return res(ctx.json({ message: "Deleted successfully" }));
  }),

  rest.patch("api/blogs/:id", async (req, res, ctx) => {
    const id = req.params.id;
    const reqBody = await req.json();
    const blog = blogs.find((blog) => blog.id === id);

    //Updating blog
    blog.title = reqBody.title;
    blog.body = reqBody.body;

    //Sending response
    return res(ctx.json({ message: "Updated successfully" }));
  }),
];

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

Integrating MSW with your Application (Browser)

After creating the mock route handlers for your application, you will need to integrate MSW into your application by creating a service worker that will intercept the requests made by your application.

Note that you won’t need to write any service worker code yourself. Instead, you will use a dedicated MSW CLI to generate it.

Run the command below to generate the required service worker files:

npx msw init public/ --save

Note: If you are not working with Create React App, replace “public/” with the relative path to your server’s public directory.

Next, in your browser.js file, import setupWorker from msw and your handlers array. Like so:

//browser.js
import { setupWorker } from "msw";
import { handlers } from "./handlers";

Then, create and export a variable, worker, and initialize it by calling the setupWorker method and passing a de-structured handlers array as an argument.

Like so:

//browser.js
export const worker = setupWorker(...handlers);

Finally, conditionally import worker from your browser.js file, and activate it by calling the start method. Like so:

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

Once your service worker is enabled, “[MSW] Mocking enabled” should be displayed on your console as shown in the image below:

1

Conclusion

This tutorial covered the basics of mocking an API server using the MSW package. You can take your API mocking to a different level by implementing a mock database using the msw/data package, which you can use to mimic an actual database.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay